Analysis of ChineseAPT: RedDelta's Recent Infection Chain
The introduction
In this blog, we’ll explore a recent infection chain of malware campaign carried out by Chinese State Sponsored threat actor RedDelta to deliver customized PlugX malware.
The infection chain we gonna cover was observed in later half of 2024, which consist of MSC(Microsoft Management Console Snap-In Control) file as the first-stage component. Upon execution, the MSC file was configured to execute VBScript that download and installs a remotely hosted Windows Installer (MSI) file, this MSI file then drops a legitimate executable vulnerable to DLL search order hijacking, a malicious loader DLL written in NIM programming language, and a DAT file containg encrypted PlugX payload and displays a decoy document.
This specific infection chain was observed when RedDelta targeted Mongolian Ministry of Defense and other Southeast Asia around August 2024 as per analysis done by Insikt Group from Recorded Future in the pdf report from article Chinese State-Sponsored RedDelta Targeted Taiwan, Mongolia, and Southeast Asia with Adapted PlugX Infection Chain
Shout out to themalwareguy and Zero2Automated for asking us to analyse this malware campaign as part of bi-monthly challenge for January 2025 :)
The infection chain
Technical analysis
We’ll go through the infection chain stage by stage and observe the TTPs used, the samples can be found here Strike Ready Labs.
We’ll kick-off with the .MSC file
A hoax : MSC file
The infection chain starts with a file named Meeting invitaion.msc
presenting itself as a PDF.
This file performs a GrimeResource technique which allows attackers to execute arbitrary code in Microsoft Management Console(
mmc.exe
)
tldr; this GrimeResource technique abuses an old XSS flaw found in
APDS.DLL
to runs JScript
which uses transformNode
obfuscation(aids in evading ActiveX security warnings), which leads to an unescaped XML script, that executes an embedded VBScript
in context of mmc.exe
.
The
VBScript
instantiates an WindowsInstaller.Installer
object where wshshell.uilevel
is set 2
to perform silent installation (see Installer.UILevel Property), then download and installs a file ver.dat
hosted on Microsoft Azure cloud with the subdomain hxxps[:]//cdn7s65[.]z13[.]web[.]core[.]windows[.]net
In short, when the users clicks the MSC file, the file runs the VBScript
that downloads and install a remotely hosted MSI file ie. ver.dat
.
The reliance : MSI file
The DIE output confirms that ver.dat
is indeed an MSI Installer file.
cmd> diec.exe files\ver.dat
Binary
Installer: Microsoft Installer(MSI)
Data: Microsoft Compound
Dropped files
When examining suspicious MSI, we should start by looking at embedded files, we can use tools like as lessmsi , msidump, orca etc. to extract and view’em:
also the summary section shows the creation date of August 2024 which matches campaign timeline.
NOTE: that Creation Date/Time can be spoofed or altered. But doesn’t look like it’s been spoofed this time
With a quick triage on the files extracted, we see:
LDevice.dat
: is a file with mostly gibberish bytes, hinting possibly packed/encryptedhid.dll
: aPE32
DLL file written with NIM programming languageLDeviceDetectionHelper.exe
: a legitimate and digitally signed executable by Logitech and also Virus Total shows it as clean file:
Table inspection
MSI files are structured as relational databases used by the Windows Installer. The database consists of several tables, each serving a specific purpose in the installation process.
From malware analysis perspective - we gonna examine some of interesting inner tables. We can start by looking all actions that may happen automatically when installer runs.
Custom Action Table
CustomAction Table defines custom actions that refers user-defined command or script that runs during installation process that are usually not handled by MSI Standard Actions.
These actions can include running an executable, system command, loading a DLL, setting properties etc. Let’s see what custom actions does this MSI file defines
- the first action,
SetOblsaYFUJOqn
will set the propertyOblsaYFUJOqn
to[%LOCALAPPDATA]\FRKBrMZCk
(see Custom Action Type 51), indicating the installation directory for the files to be extracted - the second action,
WwcslyN
will set to the target executable path[%LOCALAPPDATA]\FRKBrMZCk\LDeviceDetectionHelper.exe
InstallExecuteSequence Table
Custom Actions cannot be triggered on its own, thus there must be some scheduling that dictates when to launch, there comes the InstallExecuteSequence table that defines actions to be executed in sequence.
Above screenshot shows the InstallExecuteSequence
table for this MSI file, where important actions goes like this:
SetOblsaYFUJOqn (Sequence 801)
: set a property (OblsaYFUJOqn
) to[%LOCALAPPDATA]\FRKBrMZCk
likely used by subsequent custom actions,- then InstallInitialize action and InstallFinalize action mark the beginning and end for sequence of actions that commits the system changes.
- in the end,
WwcslyN (Sequence 6601)
: means it will execute after successful commit on system changes made during installation process, where conditionNOT REMOVE
indicates not to run it during uninstallation.
In short, This MSI file drops the three files into the user’s LocalAppData\FRKBrMZCk
folder,
and launches an executable LDeviceDetectionHelper.exe
after the installation has finalized.
A betrayal to tell : legitimate executable
Now that we know LDeviceDetectionHelper.exe
is decent file,
and gonna load hid.dll
module because its vulnerable to DLL Hijacking Techniques, DLL Side Loading (T1574.002) to be specific,
this tactic has also been noticed in other operations of RedDelta (see footnotes 3 4 5).
And the Procmon
captured events reflects the same:
Now the actual point where the trigger happens goes like this:
the innocent LDeviceDetectionHelper.exe
usually calls an export from hid.dll
ie. HidD_GetHidGuid
at offset LDeviceDetectionHelper.exe+0x6DBD2
within sub_46E7A0
subroutine, as we can see below
Usually the executable would have loaded the normal C:/Windows/SysWOW64/hid.dll
, but the fact it’s vulnerable to DLL search order hijacking and does not verify the loaded module, this loads and execute the malicious hid.dll
’s export HidD_GetHidGuid
instead.
You can see the comparison between the two hid.dll
:
The disguise : NIM Loader
The hid.dll
is written using NIM language, looking at dll exports we see the disguised export HidD_GetHidGuid
, let’s see what this function does
TIP: NIM is notorious when comes to shipping symbols which makes it hard to identify even system calls. So one can use tools like NimFilt to demangle NIM symbols within your desired disassembler.
HidD_GetHidGuid subroutine
- this starts by dynamically resolving Windows API functions with a call to a subroutine
sub_6E848AAC
, that uses series of functions referenced from Nim_DInvoke Library, - like
DInvoke::get_library_address
along withDInvoke::get_function_address
supplied with hash to dynamically resolve windows functions - and has a custom api hashing routine
sub_6E847344
(ie.@getHash__687nvoke_u95@4
at offsethid.dll+0x6944
) and here’s a quick python script for de-hashing them - having function names resolved, we see it calls RegisterClassW to register WNDCLASS STRUCT which has an interesting member
WNDPROC lpfnWndProc
, looking on MSDN it says:
WNDPROC callback function : A callback function, which you define in your application, that processes messages sent to a window. The WNDPROC type defines a pointer to this callback function.
- and the callback function is set to
sub_6E847C4C
(ie._mgdtfX__8788p_u880@16
at offsethid.dll+0x724C
) which is triggered with subsequent calls related window manipulation like CreateWindowExW , ShowWindow (see Remarks for why).
- and creates a hidden Window Class name
EDIT
. Which is an identifiable attributes for PlugX . This behavior is mentioned in this Unit42 THOR PlugX Variant article
_mgdtfX__8788p_u880@16 subroutine
This will make a call to CreateThread where argument lpStartAddress
points to function sub_6E8488B8
(ie. _bxRoBaPhPJT__8788p_u824@4
at offset hid.dll+0x7EB8
) which is executed immediately as dwCreationFlags
is 0
_bxRoBaPhPJT__8788p_u824@4 subroutine
- grabs the path to
LDevice.dat
then make a call tosub_6E848545
(ie.@CtOtHXjnm__8788p_u277@12
at offset0x7B45
) that will first RC4 decryptLDevice.dat
with harcoded keyBhaPKNSALeXZBJYz
TIP: bunch of
256
constants in a subroutine is usually an indicator of RC4 algorithm used
- decrypted
LDevice.dat
is then injected to same legitimate process ofLDeviceDetectionHelper.exe
using sequence of system windows API calls likeZwAllocateVirtualMemory
,NtWriteVirtualMemory
,NtProtectVirtualMemory
, and EnumSystemGeoID where callback functionlpGeoEnumProc
points to this decrypted executable memory region.
Resulting in deploying PlugX RAT, VT detection clearly associates this file with family label korpug
, plugx
, zusy
Decoy document
It also presents user with Meeting Invitation.pdf
loaded in the user’s PDF viewer describing a Zoom meeting invite.
Persistence
After copying executable and malicious module to new location,a new run registry key value
SetPoint
is created for persistence, which executes LDeviceDetectionHelper.exe
upon user login.
Conclusion
RedDelta Group uses customized PlugX malware to carry out its espionage with evolving Tactics, Techniques & Procedures(TTPs) like abusing legitimate and digitally signed binaries, using sophisticated MSI files to bundle all the required files, also adapting to new programming languages like NIM. Hinting RedDelta will continue its campaign with evolved infection chains.
Also steps taken by authorities in response of Chinese APTs and PlugX like Justice Department and FBI Conduct International Operation to Delete Malware Used by China-Backed Hackers
Automation script
api name resolve script
import pefile
def custom_hash(input_str):
hash_value = 0xC0DE1337
i = 0
while True:
chr_value = ord(input_str[i]) if i < len(input_str) else 0
if not chr_value:
break
hash_value ^= chr_value + ((hash_value >> 8) | (hash_value << 24) & 0xFFFFFFFF)
i += 1
return hex(hash_value & 0xFFFFFFFFFFFFFFFF)
def api_hash_resolve(dllToLoad, hashToResolve):
pe = pefile.PE(f'C:\Windows\SysWOW64\{dllToLoad}')
for exp in pe.DIRECTORY_ENTRY_EXPORT.symbols:
if exp.name is not None:
if custom_hash(exp.name.decode()) == hex(int(hashToResolve, 16)):
print(exp.name.decode())
def main():
dllList = ["kernel32.dll", "ntdll.dll", "user32.dll"]
print(f"DLLs Available: \n0: {dllList[0]}\n1: {dllList[1]}\n2: {dllList[2]}")
dllNumber = int(input("Choose DLL number: "))
hashToResolve = input("Hash(hex) to Resolve: ")
api_hash_resolve(dllList[dllNumber], hashToResolve)
if "__main__" == __name__:
main()
Indicatior of Compromises (IOCs)
FILE NAME | SHA256 |
---|---|
Meeting invitaion.msc |
ca0dfda9a329f5729b3ca07c6578b3b6560e7cfaeff8d988d1fe8c9ca6896da5 |
ver.dat MSI file |
2d884fd8cfa585adec7407059064672d06a6f4bdc28cf4893c01262ef15ddb99 |
LDeviceDetectionHelper.exe |
282fc12e4f36b6e2558f5dd33320385f41e72d3a90d0d3777a31ef1ba40722d6 |
hid.dll the NIM loader |
1a37289c70c78697b85937ae4e1e8a4cebb7972c731aceaef2813e241217f009 |
LDevice.dat |
37c7bdac64e279dc421de8f8a364db1e9fd1dcca3a6c1d33df890c1da7573e9f |
domain used to downlaod MSI file | hxxps[:]//cdn7s65[.]z13[.]web[.]core[.]windows[.]net |
decrypted LDevice.dat ie. PlugX malware |
d7a4255297c91d26d726dec228379278b393486e6fa8a57b9b7a5176ca52f91e |
Meeting Invitation.pdf |
9d844b275725e6241f6e70d17ed68bb7eb90b684832ef5952a91a5040ffe5d94 |
connection made | hxxps[:]//conflictaslesson[.]com[:]443 |
MITRE ATT&CK Techniques
TACTIC: TECHNIQUE | ATT&CK CODE |
---|---|
Initial Access: Phishing - Spearphishing Attachment | T1566.001 |
Initial Access: Phishing - Spearphishing Link | T1566.002 |
Execution: User Execution - Malicious File | T1204.002 |
Execution: Command and Scripting Interpreter: Visual Basic | T1059.005 |
Persistence: Boot or Logon Autostart Execution — Registry Run Keys | T1547.001 |
Defense Evasion: Hijack Execution Flow - DLL Search Order Hijacking | T1574.001 |
Defense Evasion: Hijack Execution Flow - DLL Side-Loading | T1574.002 |
Defense Evasion: Deobfuscate/Decode Files or Information | T1140 |
Defense Evasion: System Binary Proxy Execution: MMC | T1218.014 |
Defense Evasion: System Binary Proxy Execution: Msiexec | T1218.007 |
Defense Evasion: Masquerading: Match Legitimate Name or Location | T1036.005 |
Defense Evasion: Masquerading: Double File Extension | T1036.007 |