Back www.sophos.com Family Tree Dll Sideloading Cases May Be Related
We have observed multiple attacks targeting government organizations in Asia, all involving DLL sideloading – historically a favorite technique of China-based APT groups — as far back as 2013 and as recently as 2020 . In this article, we look at the evidence that connects five of them, showing how threat actors base their attacks on well-known, effective techniques, adding complexity and variation over time. Understanding how cases are related helps defenders (and customers) think not just who’s doing the attacking, but what kind of threats may be afoot – and, naturally, how to prioritize analysis and defense for best results.
In the most interesting of the five cases, a USB worm infected organizations in Southeast Asia. This worm copies everything it finds in specific directories when replicating itself, including components of other APT attacks by Mustang Panda and LuminousMoth . We don’t have any evidence that the three APTs are linked, and we also know that multiple USB worms, when infecting systems simultaneously, may inadvertently combine their files. (This is similar to macro virus mating , a phenomenon identified over twenty-five years ago .)
The case involving the USB worm has significant overlap with the other four cases we observed, including loader DLLs using the same kind of code flow obfuscation and identical loader shellcode. We can’t be sure that it’s the same threat actor behind both the USB worm case and the other attacks – it may be different threat actors with access to the same tooling – but the similarities are compelling.
We’ll take a deep dive into all five cases, further detailing the infection timeline of the USB-worm attack in an appendix. We’ll spotlight a piece of shell code that seems to be the common thread in all five cases, and then dig into extended step-by-step breakdowns of seven scenarios we associate with these cases. We’ll close with indicators of compromise associated with these cases, which we will also make available on our GitHub.
Before all that, though, it’s worth briefly defining what DLL sideloading is, as it’s often confused with a similar attack called DLL preloading.
DLL sideloading and preloading (sometimes known as -order hijacking) are both attacks that hijack execution flow, although there is a subtle distinction between them.
DLL preloading (AKA order hijacking) – T1574/001
DLL sideloading – T1574/002
A number of user reports led us to initially spot the threat actor’s activities. We’ll start with the most basic case and progress to the four more complex examples.
This was the case that first drew our attention to the malicious server 91.245.253[.]52, which appears repeatedly in these attacks.
This case came to light thanks to a stager alert (DynamicShellcode) received from a customer. The malicious payload (SSCE5532.dll) was executed via the command prompt, as shown in the following process trace:
The threat actor placed the malicious DLL on the desktop. It executed shellcode for a standard Metasploit (or, possibly, Cobalt Strike) reverse HTTP shell, connecting to the following attacker-controlled server:
We started looking for other cases involving the 91.245.253[.]52 server, and we found them. This one involves two DLL sideloading attacks.
The initial infection consists of ciscocollabhost.exe, a clean and digitally signed Cisco application that, on execution, loads ciscosparklauncher.dll, a malicious DLL.
Our telemetry indicates that ciscosparklauncher.dll is a loader and that the payload could be a file named 2831329086.inf, located in the same directory.
, a password-protected RAR archive is downloaded from a distribution server and unpacked, as shown in these command lines:
The RAR archive contains the following files:
, the executable googleupdate.exe (which, despite its name, has nothing to do with Google; it’s a clean, digitally signed VLC Media Player application) in c.rar is used to sideload libvlc.dll, a malicious loader that loads the payloads from the encrypted implants in the archive.
Conveniently, those implants write out detailed debug logs on their progress:
p1-p11: privilege escalation progress messages x1-x4: module execution progress messages
Figure 1: Events conveniently logged
Once this second sideloading attack is complete, the malware connects to the stager server, this time over port 443.
This attack was detected by Sophos’ HeapHeapProtect dynamic-shellcode mitigation, which prevents code running in heap space from adding arbitrary code into the memory space of the original application, and similarly prevents lateral code injection into other applications (and flags the attempt). As in the case, this attack featured two sideloading attempts. In fact, the first was exactly the same as seen in the two cases.
The first attack featured the same executable and malicious DLL as we saw in the other cases, and we once again observed a connection to 91.245.253[.]52. came the downloading and unpacking of a password-protected RAR file, using a different distribution server:
The walk.rar archive contained six files: three encrypted implants and an encrypted config (all with a PLG extension), a clean executable (Netsky.exe, a Razer Chromium Render Process), and a malicious DLL (RzLog4CPP_Logger.dll).
The second attack used the loader Netsky.exe and the malicious RzLog4CPP_Logger.dll from the first part of the attack, which decrypts and loads alloc.plg, one of the encrypted implants. In turn, this implant loads the others.
We also noted that the attacker executed 2.exe, with the path of NetSky.exe as an argument. The function of this executable is currently unclear.
We found this case by running a VirusTotal RetroHunt using the characteristics of the sideloading DLLs we spotted in the cases. Of the five cases we’ll cover, this could be considered the most complex, and we will return to it later in this article when we do a deeper analysis of infection timelines for these cases. It includes three sideloading efforts.
We noted a significant code overlap (especially in the loader shellcode) between this case and the other sideloading cases discussed so far, so we think this was also run by the same threat actor. However, the payload turned out to be totally different: a USB worm. We’re uncertain as to the purpose of this worm. It collects all files from the root of the USB drives and copies them as the infection spreads to other devices. It could be a deliberate data exfiltration method, or just an unwanted side effect of the propagation process.
In this case, the threat actor used a clean usbconfig.exe executable using multiple names (disk_watch.exe, usb drive.exe, and Removable Disk.exe); an encrypted implant (usb.ini); and u2ec.dll, a malicious loader for the implant.
In Case 4, we observed sideloading components from two other APT groups — Mustang Panda and LuminousMoth – in the same directory as files from the original threat actor. We think that the presence of these two additional APTs is collateral damage during the file-collection process, rather than an indication of collusion.
The files corresponding to the sideloading attack included disk_watch.exe and u2ec.dll. Files corresponding to Mustang Panda included rzlog4cpp.dll (a Mustang Panda reverse shell, not to be confused with the RzLog4CPP_Logger.dll we saw in Case 3), wuwebv.exe (a clean but renamed copy of Netcat), and two DLLs that were clean dependencies of Netcat.
The rzlog4cpp.dll establishes a reverse shell by invoking the Netcat component with the following command line:
Files corresponding to LuminousMoth included msbuild.exe, a clean Silverlight launcher; and version.dll, a malicious DLL. The latter file is also a USB worm, operating in a similar way as the usb.ini implant mentioned previously in Case 4. It is associated with LuminousMoth APT activities seen in 2021.
We identified one other component, a clean copy of Microsoft’s WinWord.exe. Its role is unknown, although Kaspersky researchers have speculated that it may have been used to sideload a malicious DLL, wwlib.dll.
The last case we’ll examine involved three different sideloading attacks, as Case 4 did (though no worm was detected). We covered the first two attacks in Case 3, although we noticed a slight difference this time. The “Triple Threat” also has echoes of Case 2, as you’ll see.
As in Case 2, the threat actor used ciscocollabhost.exe and ciscosparklauncher.dll, and downloaded, unpacked, and executed c.rar from 5.252.178[.]162/IJOINOIS.
However, this time the threat actor also downloaded and executed an additional password-protected RAR archive, v1.rar, from 103.253.72.[.]116/_akjsdnfkjsnjfekse. (We saw that IP address already, in Case 3.) v1.rar contains clean copies of smstore.exe and msvcrt.dll (both legitimate Microsoft files) and SYSMSRV.dll, a malicious DLL.
This attack used googleupdate.exe (the clean VLC executable) and libvlc.dll, a malicious DLL, as described in Case 2.
This attack used the same files from v1.rar, although the threat actor also used a UAC bypass trick to execute commands – including an unidentified file, 3.exe. (We’ll detail this bypass trick below as Scenario 5.) As with “2.exe” in Case 3, the purpose of this executable is unknown.
We’ve lain out five cases; let’s look at the common threads.
First, the malicious server 91.245.253[.]52 – our first clue in the investigation, as noted in Case 1 — made an appearance in every case. Other interesting traces are shown in the chart below.
Table 1: Various traces and IoCs noted among the five DLL sideloading cases
More significantly, when the sideloader DLL decrypts the plugin, it follows the execution by jumping to the first byte of the file. The file content of the decrypted plugin starts with a short PE loader shellcode, which loads the encrypted plugin DLL. This loader shellcode is the same in all seven scenarios described in the following sections, which establishes a strong connection among them.
Figure 2: The shared loader shellcode
Similar to PlugX loaders , this shellcode loader overwrites the first 0x1000 bytes of the decrypted and loaded plugin DLL with zero bytes.
Figure 3: The loop that fills the first 0x1000 bytes with zero bytes
Moving on, we’ll dissect some of the more interesting activities we spotted during analysis. We’ll dissect one scenario from each of our five cases, and look in addition at two earlier finds that appear to be related to these cases. We should note that normally, we’d expect to see one scenario (clean loader + malicious loader + plugins) per case, but a couple of these cases literally doubled up. (Why they would do that is left as a conjecture for the reader.) For ease of reference, we’ll letter our scenarios – A, B, C, D, E – and identify the case to which it is related.
We discovered the two “extra” scenarios – F, G — by taking the information we had from our five cases and looking beyond our own data to see what other defenders might have already discovered but not yet flagged as part of a larger threat. They’re presented here to show how else these attacks might present to threat hunters, and to give some indication of just how long-running the threat might be.
Another of the interesting variations we found in this set of cases is that similar or identical configuration data is stored in multiple plugins. We’ll show this in detailed analysis of the specific plugins.
This was the initial infection, which consisted of the following components:
The implant had the name 2831329086.inf, and was placed in the same directory as the sideloader DLL. We don’t have the implant, so we can only guess at its behavior based on the activity logs.
The files belonging to this scenario were found in the downloaded c.rar described in Case 2.
This campaign was dubbed “Cool Client” by its developers, based on leftover development information in the components.
Compile time: 2021-May-10 19:40:05
PDB path: G:\project\木马\ Cool\Client \hijack_export\libvlc\Release\libvlc.pdb
PDB File Name : G:\project\木马\Cool\Client\hijack_export\libvlc\Release\ libvlc.pdb (Translation of the Chinese text: Trojan horse )
Most of the libvlc exports are dummy (RET) functions that immediately exit — except for libvlc_new, which is the main function.
Figure 4: libvlc_new has a few things going on, in fact
The DLL has a default config structure. The config data is stored in a memory region. First it is initialized with the hardcoded config, and then this region is overwritten with whatever the decrypted content of time.sig isThe first value looks like an ID string for the config structure, the second one is an encryption key, and the third one should be the C2 address.
These are default values, as indicated by the RFC 1918 IP address. To update with the real values, the process loads time.sig and decrypts the config info from it, overriding the default configuration with the target system’s actual configuration. During this process it:
These are the encrypted modules that are loaded and executed during the infection process.
Compile time: 2021-May-31 01:23:24
It appears to contain default config data, similar to libvlc.dll, and is likewise overwritten via time.sig
Relevant strings from the embedded config structure:
Another internal IP address, 192.168.211.13, is stored elsewhere.
The implant employs a UAC bypass using the CMSTPLUA COM interface , and injects the created process into winver.exe. Processes are created for these files:
Compile time: 2021-06-03 01:28:52
PDB File Name: G:\project\木马\Cool\Client\main\Release\main.pdb (Translation of the Chinese text: Trojan horse )
This file refers to several source files in its code, including:
As with examples, this implant contains default config data:
as well as the internal IP address 192.168.211.13.
This plugin registers the clean loader executable for autostart as a service. (As flagged above, this is a service claiming to be Google Update, but is actually a VLC media player executable.)
Figure 5: The new “service” gupdaten
We didn’t obtain these implants. All we know is that loader.ja refers to them and would load them if they existed,
This file contains encrypted config information, as shown in Figure 6:
Figure 6: A look at the hex related to machinetimeer
Code analysis shows that this scenario is built around vtcp.dll (the entirety of which is actually embedded into the main implant; it’s not just that the source code is linked into the plugin!) from the Trochilus RAT collection. These files were in the downloaded walk.rar archive.
Compile time: 2021-Aug-19 21:40:13
This contains a digital signature, seemingly from Google LLC (but really another self-signed fake):
Figure 7: A certificate that’s not what it claims to be
Thumbprint: 747EC25FDC3710E46D69135FAE8797718B967E25 Algorithm: sha256RSA Valid from: 5:52 AM 5/10/2021 Valid to: 5:52 AM 5/10/2023
This uses the same code flow obfuscation as libvlc.dll. It loads and decrypts alloc.plg.
Compile time: 2021-Aug-19 22:38:47
Contains an encrypted embedded PE, which has a Chinese PDB string:
Compile time: 2018-Feb-10 19:04:13 PDB File Name : G:\ROOT\代码工程\木马\技巧收集\38dll\Release\38dll.pdb
(Translation: G:\ROOT\Code Project\Trojan\Trick Collection\38dll\Release\38dll.pdb)
The implant executes wusa.exe (and possibly grabs its process token).
As Microsoft describes it, this creates a new process and its primary thread; the new process runs the specified executable file in the security context of the specified credentials (user, domain, and password). It can optionally load the user profile for a specified user. Abuse of this technique was previously noted by researcher Vitali Kremez in 2018 and is associated with the Tofsee plugin-based spambot. It is probably a Vault7 fileless AlwaysNotify UAC bypass, similar to this one .
Figure 8: A UAC bypass in action
username: uac domain: is password: useless
This hollows free.plg into dllhost.exe. ( Hollowing , mentioned also in Scenario 2, is an attack in which a threat actor removes code in an executable, in this case dllhost, and embeds malicious code in order to trick the target machine into running the “trusted” executable.) Possible command-line parameters, which are passed to the clean loader when executed, include:
passuac online install
This uses UAC_Bypass_CMSTPLUA and creates a service (InstallSvc) with the command ‘C:\ProgramData\Netsky\NetSky.exe online’.
Compile time stamp: 2021-Aug-19 21:21:29
This stops the current service, creates the event Global\\ACT, then calls the sendSAS function of sas.dll with parameter 0. It then loads local.plg (if the service was found) and main.plg, all expected in C:\\ProgramData\\Netsky. main.plg is hollowed into a dllhost.exe process.
It opens C:\\ProgramData\\Netsky\\vs_session.dat, which appears to be a flag file (though we were unable to recover it for examination). If the file is not present, the process keeps checking in a loop.
This contains the encrypted config, using a different encryption method than the implants. The decoded data contains these strings:
Figure 9: A screenshot taken from a memory dump
Compile time stamp: 2021-Aug-19 21:04:16
During installation this implant is hollowed into dllhost.exe. It contains the embedded vtcp.dll from the Trochilus RAT collection .
This DLL is loaded into memory, gets the exports from vtcp.dll, and uses them later in communication.
vtcp.dll uses CNetDiskClientSocket vftable functions for communication. It reads in and decrypts local.plg. It has a predefined hardcoded data structure that is overwritten with the decoded content. This hardcoded structure could be used in testing, or when there is no local.plg file found. The content of this hardcoded config is:
It registers the application as class “MSN Shessll – %d”; the number is generated by a call to the Windows API function GetTickCount. , it logs keystrokes to a file. It creates dir.dat in both C:\\ProgramData\\Netsky and C:\Users\All Users. Both files will contain the name of the directory where the sideloading components were installed, in this case, C:\ProgramData\Netsky.
The process generates debug logs during execution, as shown in Figure 10:
Figure 10: Logs generated by the process
Based on the internal development info stored in the files, this scenario goes by the code name “U Disk Hijacking.”
Contains the PDB path:
G:\project\APT\U盘劫持\new\u2ec\Release\u2ec.pdb
(Translation of Chinese text: U Disk Hijacking)
We found a variation of this file on VirusTotal. The only difference is some appended data:
The implants are stored in an encrypted format. They are decoded by the loader shellcode. These implants are DLL files with no exports; the main code is the entry code.
Interestingly, this artifact does not appear to do any C2 communication.
PDB File Name : G:\project\APT\U盘劫持\new\shellcode\Release\shellcode.pdb
(Chinese text: U Disk Hijacking)
The icon and the name of the executable spoofs a removable drive icon, thus tricking the victim into clicking on it. The directory listing would look as shown in Figure 11:
Figure 11: The spoofed icon
Then a warning may be displayed:
Figure 12: Windows flags the attempt to run the file, but the information the user sees inspires trust
However, because the executable is (apparently) clean and signed, the victims is not suspicious.
If it is not running from a path that contains ‘programdata’, it infects the computer and proceeds to do the following creation and copying actions:
It creates the installation directory udisk and copies document/image files there, then copies every file from the current directory (directory of GetModuleFileName) to c:\programdata\udisk.
It creates the following autorun key in the registry:
It then copies itself to the following locations and executes those copies:
If it is running as disk_watch.exe, it infects USB disks by replicating itself there.
This is the timeline of the infection process from the logs:
First, u2ec.dll loads the payload:
Then, files (documents created in the root by the user rather than the worm itself) are copied to the installation directory, as are instances of the worm and components of the other sideloading scenarios. After all that, an autorun registry key is created:
The following components were used:
In this case there is no additional encrypted payload file; the sideloaded DLL, symsrv.dll, is the payload itself.
C:\Users\admin\Desktop\djwklqjdlwqjldwqjlkfjwlqkjlqwjglqwjglqjlgjwqkjgk\SYMSRV.pdb
This is a 64-bit loader DLL that does a UAC bypass trick to execute commands, including the unidentified 3.exe component, as explained in this blog from PenTestLab .
The implant executes various commands, which are inserted into the registry key HKCU\ Classes\ms-settings\CurVer . It then tries to execute two different Windows components, both of which are vulnerable to the UAC bypass method:
When these clean Windows components are executed, they read the command to be executed from the registry key and run it with higher privileges.
We observed the following commands executed in this fashion:
The implant needs to make sure that another execution will not interfere, so it creates a flag in the registry: if it is set, some other command is in progress. The flag key is HKCU\Classes\aaabbb32\shell\open\command .
The threat actors show strong devotion to the DLL sideloading technique here. This UAC bypass method could have been compiled into any of their implants; instead, the simple logic has been implemented as a standalone sideloading scenario, and the debug features exploded the payload to a huge (1.1MB) DLL file.
As mentioned, a VirusTotal hunt led us to additional cases from non-Sophos sources. This case was found via VT hunting; it dates from January 2021, but the shared characteristics clearly connect it to our cases from 2022.
The following file was identified:
Signed by a self-signed digital signature claiming to originate from, but not actually originating from, Bitdefender:
Figure 13: The questionable digital certificate
It loads the payload from the file qutmain.dat.
This sideloading scenario consists of the following files:
Here, the .dat files are the encrypted plugins; quttpy.exe is the clean loader from Bitdefender (Bitdefender Crash Handler). The use of this clean file in sideloading scenarios has been reported (though with different payload files) since early 2021 and attributed to ShadowPad, aka NetSarang . (Additionally, Trend Micro detects the encrypted .dat files as Trojan.Win32.SHADOWPAD.CGW.enc.) There is an additional file info.dat, which should contain the actual configuration data, but we weren’t able to recover it.
The log.dll loader uses the same obfuscation as the earlier cases, and the decrypted plugin files used the same shellcode loader. We believe this is a reasonably strong connection with the campaigns in 2022.
This file is essentially the same as alloc.plg. It refers to the following locations where the installed plugins are stored:
It generates similar debug messages with the [fortest] tag as well, and contains exactly the same UAC bypass component.
This is the same as free.plg; only the file paths have changed to reflect the different scenario.
This is the same as main.plg. It contains the same hardcoded default config values:
This is the encrypted configuration file. We haven’t been able to recover it.
This is a very early sample (from 2017!) that shows the same obfuscation as the newer cases, as well as a similar default hardcoded configuration. It was found via VT.
Contains an embedded executable:
Some characteristic strings from the embedded executable:
Also, a similar config data is stored, but with a different marker at the beginning:
Sideloading components could be identified from the code:
The malicious DLL is attributed as gh0st RAT. Details of the payload are unavailable at this time.
From our telemetry data we reconstructed the steps of the infection process. Here’s the timeline for Case 4 (“Worm Circus,”) the most complex attack. This is the one that both delivered a USB worm in its payload and ingested portions of other APTs:
Execution of initial sideloading:
Unpacking the files of the second sideloading:
This shows execution of the second sideloading attack, which creates registry keys to register a custom file extension and a custom command to open files of that extension. This could be a persistence tactic, similar to how SolarMarker does it .
Creation of yet another 3.exe file (symstore.exe -> fodhelper.exe -> 3.exe)
Fodhelper.exe executes 3.exe. But before that, the registry key HKEY_USERS\S-1-5-21-1497078658-3044148255-4064547459-1001_Classes\ms-settings\CurVer is created. This is likely a UAC bypass method similar to the one Pentestlab described in 2017 and more recently used by Trickbot.
The threat actor then executed 3.exe, which deletes the components of sideloading scenarios. Note the presence of the files nvsmartmax.dll and nvsmartmax.dat. Cybereason has previously reported that they are used by a Chinese APT group in their attacks.
Downloading the components of the third sideloading scenario:
Unpacking the files from the downloaded RAR archive:
Execution of the third sideloading scenario:
Connecting to the server:
The threat actor executed symstore.exe, with a few different command line arguments:
It is likely that the sideloaded DLL component (symsrv.dll) takes these command-line parameters and executes using the fodhelper.exe UAC bypass trick.
IOCs for these attacks will be available on our GitHub repository .
The full story
This article is one source in a clustered incident — the cluster page carries the summary, timeline and every other outlet covering it.
