< Back to blog
critical🎯APT
publishedMarch 12, 2026

Kimsuky's Five-Stage GrimResource Loader: When an MMC File Becomes a Shellcode Injector

Threat Actors:APT43### Kimsuky / APT43 Indicators
#apt#phishing#c2#exploit#spearphishing

TL;DR: A Kimsuky (APT43/Velvet Chollima) operation was caught deploying a five-stage loader chain that begins with a single .msc file -- a Microsoft Management Console configuration file -- and ends with 1MB of x86 shellcode injected directly into memory. The chain uses the GrimResource technique for initial execution, then cascades through JScript decryption, .NET BinaryFormatter deserialization (SortedSet gadget chain), XAML-based shellcode injection via XamlReader.Parse(), and finally native code execution via Marshal.GetDelegateForFunctionPointer(). The C2 infrastructure spans 14 IP addresses across UCloud HK (AS135377) and DAOU Technology (AS45996) in Seoul, Korea, using three dynamic DNS providers (dynv6.net, dns.army, v6.navy) for domain rotation. Every C2 server runs the same Apache 2.4.58 / PHP 8.0.30 / OpenSSL 3.1.3 stack -- a deployment fingerprint so uniform it could only come from automated provisioning.


A .msc File Walks Into a Network

When most security teams think about dangerous file types, .msc files do not make the list. They should.

Microsoft Management Console files are XML documents that configure administrative snap-ins -- things like Device Manager, Disk Management, Event Viewer. They are opened by mmc.exe, a signed Microsoft binary. They are generally trusted by email gateways, endpoint protection, and users alike.

Which is exactly why Kimsuky chose them.

The sample -- plugin.msc, 5.9MB, uploaded to MalwareBazaar from Hungary on March 11, 2026 -- is a valid MMC Console File that opens normally in mmc.exe. What the user does not see is the XSL Transform payload embedded in the XML StringTable node, wrapped in a unescape() call. When the MMC engine processes the stylesheet, it hits a <ms:script> block containing JScript, and the chain begins.

This is the GrimResource technique, and Kimsuky has made it their own.

What Was Found vs. What Was Known

AspectPrior ReportingOur Findings
Delivery mechanismKimsuky known for HWP, DOC, CHM lures.msc file using GrimResource technique
Execution chain depthTypical 2-3 stage loaders5 distinct stages: MSC β†’ JScript β†’ .NET deserialization β†’ XAML β†’ shellcode
ObfuscationStandard encoding/encryptionCustom BUxBF cipher (split-alphabet Base64 + XOR), reversed base64 with space padding, compressed shellcode
Gadget chainKnown .NET exploitationSortedSet/TypeConfuseDelegate chain redirecting Compare() to XamlReader.Parse()
C2 infrastructureSeoul-based hosting14 IPs across 2 ASNs (UCloud HK, DAOU Technology), uniform Apache/PHP/OpenSSL stack
Domain rotationFree DNS servicesThree providers simultaneously: dynv6.net, dns.army, v6.navy
Related samples.msc files increasingly popularCampaign cluster: Smart_Policing, Cyber-Advisory, Scheme_Application_Form

The Five-Stage Execution Chain

[Stage 1: plugin.msc]
    MMC Console File (XML, 5.9MB)
    GrimResource: XSL Transform in StringTable
    SW_HIDE flag β€” window hidden on launch
        |
        v
[Stage 2: JScript Loader]
    BUxBF() decryption function
    Split-alphabet Base64 + XOR cipher
    Unique hex key per encrypted call
    Forces .NET 4.0 CLR via COMPLUS_Version
    Builds 1.1MB payload via 50+ string concatenations
        |
        v
[Stage 3: .NET Deserialization]
    BinaryFormatter.Deserialize()
    SortedSet<string> gadget chain
    TypeConfuseDelegate trick:
      String.Compare() β†’ XamlReader.Parse()
    1,138,987-byte serialized object
        |
        v
[Stage 4: XAML Shellcode Injection]
    ResourceDictionary (1,136,894 bytes)
    Base64 decode β†’ GZip decompress β†’ 1,004,940 bytes
    VirtualAlloc (PAGE_READWRITE)
    Marshal.Copy (write shellcode)
    VirtualAlloc (PAGE_EXECUTE_READ)
    Marshal.GetDelegateForFunctionPointer()
        |
        v
[Stage 5: x86 Shellcode]
    1,004,940 bytes of MSVC-compiled native code
    Anti-analysis prologue (xor eax; sub; inc; jz)
    XOR key 0xACC2D51A for config decryption
    Runtime API resolution: Sleep, VirtualAllocEx, CreateThread
    C2 communication (encrypted at runtime)

Every stage is designed to evade a different layer of defense. Stage 1 bypasses file type restrictions. Stage 2 evades static string detection. Stage 3 exploits trusted .NET deserialization. Stage 4 uses XAML -- a UI markup language -- to inject shellcode. Stage 5 encrypts its own configuration to prevent network IOC extraction.

Stage 2: The BUxBF Decoder in Detail

The JScript embedded in the XSL stylesheet is not just obfuscated -- it uses a custom encryption scheme:

Encrypted CallDecrypted ValuePurpose
BUxBF(0)WScript.ShellShell object creation
BUxBF(1)ProcessEnvironment access
BUxBF(2)COMPLUS_Version.NET CLR version forcing
BUxBF(3)v4.0.30319Specific CLR version
BUxBF(4)System.Text.ASCIIEncodingText encoding
BUxBF(5)System.Security.Cryptography.FromBase64TransformBase64 decode
BUxBF(6)System.IO.MemoryStreamMemory stream
BUxBF(7/8)System.Runtime.Serialization.Formatters.Binary.BinaryFormatterDeserialization engine

The algorithm works in two passes: gZKYc() performs custom Base64 decoding using a split alphabet (the Base64 character table is fragmented across the code to evade signature-based detection), then rkTNf() applies XOR decryption using a repeating key unique to each call. Each invocation of BUxBF() uses a different hex key -- for example, 023389aee2418eee -- ensuring that no two decrypted strings share a key.

The critical trick is the COMPLUS_Version environment variable set to v4.0.30319. This forces the .NET 4.0 CLR to load, which is required for the deserialization gadget chain in Stage 3. Without this, the payload would fail on systems with newer .NET versions as the default runtime.

The payload itself -- variable XGMtr -- is assembled through 50+ string concatenations with deliberate space characters inserted between chunks. The spaces are stripped, the string is reversed, Base64-padded, and decoded, producing a 1,138,987-byte .NET BinaryFormatter serialized object.

Stage 3: The Gadget Chain

The deserialization payload uses the SortedSet/TypeConfuseDelegate gadget chain -- a technique from the ysoserial.net toolkit:

BinaryFormatter.Deserialize()
  β†’ System.Collections.Generic.SortedSet<string>
    β†’ System.DelegateSerializationHolder
      β†’ Comparison<string> delegate
        β†’ System.Windows.Markup.XamlReader.Parse(string)

The elegance is in the type confusion. SortedSet<string> needs a Comparison<string> delegate to sort its elements. The gadget chain replaces the legitimate comparison delegate with one that calls XamlReader.Parse() instead of String.Compare(). When the SortedSet is deserialized and tries to sort its contents, it does not compare strings -- it parses one of them as XAML.

This effectively turns the .NET framework against itself. The entire chain runs inside trusted .NET code. No PowerShell. No cmd.exe. No suspicious process creation.

Stage 4: XAML as a Weapon

The XAML ResourceDictionary (1,136,894 bytes) performs in-memory shellcode injection using only .NET framework classes:

  1. Convert.FromBase64String() decodes 849,582 bytes of compressed data
  2. GZipStream decompresses to 1,004,940 bytes of x86 shellcode
  3. VirtualAlloc(IntPtr.Zero, size, 0x1000, 0x04) allocates memory as PAGE_READWRITE
  4. Marshal.Copy() writes the shellcode into allocated memory
  5. VirtualAlloc(ptr, size, 0x1000, 0x20) changes protection to PAGE_EXECUTE_READ
  6. Marshal.GetDelegateForFunctionPointer() + Action.Invoke() executes

The memory protection change from RW to RX (rather than allocating as RWX directly) is a deliberate evasion technique. Many EDR products flag single-step PAGE_EXECUTE_READWRITE allocations but are less likely to alert on a two-step RW→RX transition.

The XAML itself uses Microsoft.VisualBasic.Interaction.CallByName() for API resolution -- another layer of indirection that avoids direct import references.

The C2 Empire: 14 Servers, 3 DNS Providers, 1 Fingerprint

C2 IP Addresses

IP AddressPortASNOrganization
118[.]194[.]248[.]246443AS135377UCloud HK
152[.]32[.]243[.]17880AS135377UCloud HK
101[.]36[.]114[.]6680β€”Korea
27[.]102[.]137[.]14080AS45996DAOU Technology
152[.]32[.]138[.]146443AS135377UCloud HK
118[.]194[.]248[.]134443AS135377UCloud HK
118[.]193[.]69[.]19443AS135377UCloud HK
152[.]32[.]139[.]14980AS135377UCloud HK
167[.]88[.]166[.]204443β€”β€”
118[.]194[.]248[.]18380AS135377UCloud HK
152[.]32[.]243[.]21580AS135377UCloud HK
27[.]102[.]138[.]125β€”AS45996DAOU Technology
118[.]194[.]249[.]109β€”AS135377UCloud HK
101[.]36[.]114[.]231443β€”β€”

Fourteen C2 servers. Nine on UCloud HK (AS135377), two on DAOU Technology (AS45996), three unattributed. Nearly all resolve to Seoul, South Korea. UCloud HK is a Hong Kong-registered subsidiary of UCloud, a Chinese cloud provider -- frequently observed in North Korean APT infrastructure due to its presence in South Korean data centers.

The Uniform Server Stack

Every C2 server presents the same fingerprint:

ComponentVersion
Web ServerApache 2.4.58
PHP8.0.30 (EOL)
OpenSSL3.1.3
TLSSelf-signed certificates
AdditionalRDP (3389) exposed on some

This level of uniformity across 14 servers is not manual configuration. It is automated provisioning -- likely a deployment script or container image that Kimsuky rolls out to each new C2 node. The use of PHP 8.0.30 (end-of-life since November 2023) as a consistent choice further suggests a frozen deployment template that has not been updated.

Dynamic DNS Rotation

ProviderDomainsPurpose
dynv6.netlink-nid-log.*.dynv6.net, elecviews85.dynv6.net, mhjjh.dynv6.netC2 domain rotation
dns.armyndocs0link.dns.army, 3tg8i.dns.armyC2 domain rotation
v6.navya7f3q.v6.navyC2 domain rotation

Three free dynamic DNS providers used simultaneously. If one is blocked or taken down, the others continue operating. The subdomain patterns (link-nid-log, elecviews85, a7f3q) appear randomly generated, suggesting automated domain creation.

Attribution: Kimsuky / APT43

The attribution chain:

  1. YARA match: Detect_Kimsuky_APT_Malware (by daniyyell) on MalwareBazaar
  2. ThreatFox tags: All C2 IOCs tagged win.kimsuky with first_seen dates March 1-10, 2026
  3. GrimResource adoption: Consistent with Kimsuky's Q1 2026 TTP evolution
  4. Infrastructure pattern: Seoul-based hosting via UCloud HK and DAOU Technology matches known Kimsuky preferences
  5. Dynamic DNS: Free DNS services for C2 rotation is a long-documented Kimsuky behavior
  6. Related MSC samples: Campaign cluster includes Smart_Policing_Industry_Participation.msc, Cyber-Advisory-2026.pdf.msc -- government/policy themes consistent with Kimsuky targeting

MITRE ATT&CK Mapping

TacticTechniqueIDImplementation
Initial AccessSpearphishing AttachmentT1566.001.msc file delivered via email
ExecutionSystem Binary Proxy ExecutionT1218mmc.exe processes XSL transform
ExecutionXSL Script ProcessingT1220GrimResource XSL in StringTable
ExecutionJScriptT1059.007BUxBF decoder in XSL stylesheet
Defense EvasionObfuscated FilesT1027BUxBF cipher, reversed base64, space padding
Defense EvasionDeobfuscate/DecodeT1140Multi-stage decoding chain
Defense EvasionProcess InjectionT1055VirtualAlloc + shellcode copy + execute
Defense EvasionTrusted Developer UtilitiesT1127.NET BinaryFormatter deserialization
C2Dynamic DNST1568.001dynv6.net, dns.army, v6.navy
C2Web ProtocolsT1071.001HTTPS/HTTP to C2 servers

Detection

Endpoint

  • Block .msc file execution via Group Policy or AppLocker
  • Monitor for mmc.exe spawning script engines (JScript, VBScript)
  • Alert on COMPLUS_Version environment variable modifications
  • Detect VirtualAlloc with PAGE_READWRITE followed by PAGE_EXECUTE_READ protection changes from non-standard processes
  • Monitor for XamlReader.Parse() calls outside legitimate WPF applications

Network

  • Block all 14 listed C2 IPs
  • Sinkhole *.dynv6.net, *.dns.army, *.v6.navy at DNS resolver level (or specific domains if blanket block is too broad)
  • Alert on connections to UCloud HK (AS135377) from enterprise networks
  • Monitor for self-signed Apache/PHP stacks on non-standard ports

File

Deploy YARA rules targeting:

  1. GrimResource MSC pattern (XSL Transform in StringTable)
  2. BUxBF decoder function signature
  3. SortedSet/TypeConfuseDelegate gadget chain
  4. XAML shellcode injection pattern
  5. ConsoleFileID 1225e4fe-5c83-4d7f-a643-606b18e5f890

Indicators of Compromise

File Indicators

# MSC Loader
SHA256: f239e3fedc4926ff3cf58f95bacff9d8f11289e58036ed507ab3f435dce1b2b1
MD5:    6db53d66629f95a2d830a4f56e8c69f2
SHA1:   253d232e1485e7e60ff3380999412c773d0a9a14
File:   plugin.msc

# Extracted Shellcode
SHA256: 95f4954ad79fa972bfd4fe217608ed5216c674e8ae6662cb8ffb31dbed50ec63
MD5:    66126fa42accfb183f72e25b20750b97

# MMC Console File ID
1225e4fe-5c83-4d7f-a643-606b18e5f890

Network Indicators

# C2 Domains (defanged)
ndocs0link[.]dns[.]army
a7f3q[.]v6[.]navy
3tg8i[.]dns[.]army
link-nid-log[.]oq7n2[.]dynv6[.]net
link-nid-log[.]oc9bk[.]dynv6[.]net
elecviews85[.]dynv6[.]net
mhjjh[.]dynv6[.]net

# C2 IPs (defanged)
118[.]194[.]248[.]246    (UCloud HK)
152[.]32[.]243[.]178    (UCloud HK)
101[.]36[.]114[.]66     (Korea)
27[.]102[.]137[.]140    (DAOU Technology)
152[.]32[.]138[.]146    (UCloud HK)
118[.]194[.]248[.]134   (UCloud HK)
118[.]193[.]69[.]19     (UCloud HK)
152[.]32[.]139[.]149    (UCloud HK)
167[.]88[.]166[.]204
118[.]194[.]248[.]183   (UCloud HK)
152[.]32[.]243[.]215    (UCloud HK)
27[.]102[.]138[.]125    (DAOU Technology)
118[.]194[.]249[.]109   (UCloud HK)
101[.]36[.]114[.]231

C2 URLs

hxxps://ndocs0link[.]dns[.]army/?naps
hxxps://a7f3q[.]v6[.]navy/
hxxps://3tg8i[.]dns[.]army/
hxxp://link-nid-log[.]oq7n2[.]dynv6[.]net/
hxxp://link-nid-log[.]oc9bk[.]dynv6[.]net/
hxxps://elecviews85[.]dynv6[.]net/?naps
hxxps://mhjjh[.]dynv6[.]net/

Recommended Actions

Immediate

  • Block all 14 C2 IPs and 7 C2 domains at network perimeter
  • Deploy YARA and Suricata rules from the investigation
  • Block .msc file attachments in email gateways
  • Hunt for mmc.exe spawning script engines in EDR telemetry

Short-Term

  • Submit abuse reports to UCloud HK (hegui@ucloud[.]cn) and DAOU Technology (infra-tech@daou[.]co[.]kr)
  • Block dynamic DNS providers at DNS resolver level
  • Monitor for COMPLUS_Version environment variable modifications across the fleet
  • Assess exposure to GrimResource technique across all .msc file handling

Strategic

  • Disable XSL processing in MMC where possible
  • Implement application control policies restricting mmc.exe to approved .msc files
  • Alert on BinaryFormatter deserialization in non-standard contexts
  • Track Kimsuky's Q1 2026 .msc campaign cluster for new samples

Published by Breakglass Intelligence. Investigation conducted 2026-03-11. One .msc file. Five execution stages. Fourteen C2 servers. A North Korean APT that turned an admin tool into a shellcode injector. Classification: TLP:CLEAR

Share: