A 3.5 KB Windows loader with one import, a split-up C2 address, and a forty-byte handshake it repeats until somebody answers. We answered with an emulated panel and the stager ran our code at high integrity, then parked where we told it to.
The archive password was infected. The executable inside was 3,584 bytes small enough that the first instinct is to check whether the extraction actually finished. It had. What followed was a complete staging loader: hash-resolved imports, a runtime-assembled C2 endpoint, an architecture check-in, a 30 MB executable scratch region, and a single-byte XOR key separating it from whatever the panel chooses to send.
The sample calls itself nothing. VirusTotal had seen it once before, as eub36.exe and windows_amd64.exe, first submitted on 2026-09-21 six days before it reached the lab. The public record had more to say once we knew where to look: this is a SNOWLIGHT Windows stager, the panel-generated delivery stub for the VShell remote-access trojan, and the specific variant it stages carries a Windows architecture tag that public reporting had described but never captured on the wire.
The rig, and what the evidence means
The analysis ran through our malbox tooling. Dynamic execution took place in the isolated VMware guest for710-win10, configured with host-only networking and no route to the internet. The guest was reverted to a snapshot before and after each run. Static analysis and Ghidra work happened on the lab host.
Throughout this post, observed means recorded during the guest runs. Static means reconstructed from the binary or decompiled code. Inferred means a plausible explanation that still needs a trace.
That distinction matters here. No successful external C2 connection occurred in our runs the endpoint was unreachable by design. What the wire captures show is the implant's half of the protocol, reproduced under our control. Where we cite third-party observations of the live server, they are exactly that: secondhand, and labelled as such.
Stage 0: the archive
ae8df01df10e0b80b7f0b150366293e2 (MD5)
Format: PE32+ GUI executable, x86-64
Size: 3,584 bytes
Sections: 3 (.text, .rdata, .pdata)
Imports: 1 — KERNEL32.dll!Sleep
Imphash: e82dd51b077167be63c004bed23d0c1e
Entropy: 4.19 overall
Signature: none
A single import of Sleep from a GUI executable is not a program that sleeps. It is a program that wants an import table too small to profile everything else gets resolved from memory. The compile timestamp reads 2023-10-26, but the family pins its timestamps (a sibling submission carries one from two weeks earlier while first appearing in the wild in 2026), so we treat it as static decoration, not provenance.
Static: four functions, one job
Ghidra found four functions. One is the resolver, two are scaffolding, and one is the show.
The resolver (FUN_14000145c) walks the PEB's InMemoryOrderModuleList, hashes each module's base name and each export name with the classic rotate-accumulate scheme h = rol32(h, 19) + c, which is ROR-13 by another name and returns the address whose hash matches the caller's constant. Mixed-case module names are normalised by uppercasing characters above 0x60 before hashing. Nothing here is novel; everything here is competent.
The loader, in order:
- Resolves
LoadLibraryAand ten more API addresses by hash, loadinguser32.dll,ws2_32.dll, andmsvcrt.dll. - Builds its C2 address at runtime by concatenating four fragments
"49.2","32.1","83.8","7"throughsprintf("%s%s%s%s"). A plainstringspass shows nothing; the endpoint never exists as a contiguous literal in the file. - Checks a marker file at
%TEMP%\log_de.logbefore anything else. - Enters a connect loop against
49.232.183.87:18080(the sockaddr carries0xa046in network byte order), sleeping ten seconds between attempts. There is no exit from this loop except success. - Sends a 40-byte check-in.
- Allocates exactly 30,000,000 bytes of
PAGE_EXECUTE_READWRITEmemory. - Receives the next payload in
0x64000-byte chunks until the server closes the connection, XOR-decoding every byte with0x99as it arrives. - Closes the socket and calls the decoded buffer.
The stager authenticates nothing. There is no signature check, no size header, no MZ validation a 30 MB window with no enforced boundary, filled by whoever answers the socket, and then executed. The panel stager is small because it delegates trust and memory safety to the other end of the wire.
The kill switch, settled by experiment
Public reporting on SNOWLIGHT's %TEMP%\log_de.log check disagrees about polarity. We settled it for this variant, with controls:
- With
log_de.logpresent in%TEMP%(content irrelevant ours held one word), the loader exited within seconds and made zero connection attempts. No SYN left the guest. - With the file deleted, the same launch produced a live process that persisted in its retry cycle.
So for the Windows stager: the file's presence terminates it. This also makes the check a defensive lever planting the file neuters this loader family on a machine and, with the caveat that we have not observed what the stage does about it, a potential canary. The polarity of the ELF variants reported by Sekoia may differ; we have not tested those.
The beacon on the wire
With the guest isolated, the implant's SYNs die at the virtual switch. To learn more than the destination, we brought the panel to it: assigned 49.232.183.87/32 as a secondary address on the guest's own NIC and ran a small listener on port 18080 inside the guest. The hardcoded endpoint now resolved to a local stack we controlled.
The implant connected and sent the same forty bytes on every run, across several detonations:
77 36 34 20 20 20 46 a0 34 39 2e 32 33 32 2e 31
38 33 2e 38 37 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
| Offset | Bytes | Meaning |
|---|---|---|
| 0–5 | 77 36 34 20 20 20 |
"w64 " — Windows x64 architecture tag |
| 6–7 | 46 a0 |
Port 18080, network byte order |
| 8–20 | 34 39 2e ... 38 37 |
ASCII "49.232.183.87" |
| 21–39 | 00 × 19 |
Zero padding |
The check-in carries the platform tag, the port, and the C2 string the implant was built with. SOCRadar's July 2026 report documented the tag scheme from exposed ELF loaders l32/l64/a32/a64, plus w32/w64 for a Windows port they noted had not been seen in public reporting. Our capture is that missing wire-level description: w64 behaves exactly like its Linux siblings raw TCP, same frame, same trust model.
Feeding it a payload
With the emulated panel in place, the remaining question was what code execution actually looks like. Rather than deliver unknown second-stage content or weaponised shellcode, we assembled our own benign x64 payload 435 bytes, position-independent, resolving LoadLibraryA and GetProcAddress from the PEB with the same rotate-13 hashing the stager itself uses, then calling WinExec and MessageBoxA by name.
The emulated C2 logged the beacon, served the payload XOR-encoded with 0x99, and closed the connection. The stager decoded it, called it, and the payload ran:
whoami /alloutput written to disk by acmd.exethe served code had spawned — observed, in the artifact pulled back from the guest.- Process context:
FOR710-WIN-VM\rem, member of Administrators, High mandatory level, BATCH logon type. On a real victim, everything the user can touch, the stager can touch, with no elevation step. - The implant then parked inside the
MessageBoxAcall still alive at collection, waiting for input on a desktop nobody could see returning only when the served code decided to. The C2 owns the thread for as long as its payload wants it.
One connection, one download, one execution, and then silence: staging is one-shot. Any persistence or sustained channel is the second stage's problem which is what a VShell core is for.
What the public record says
With the family identified, the picture assembles quickly:
- SNOWLIGHT was named by Mandiant tracking UNC5174, a China-nexus access broker, in campaigns against F5 BIG-IP and ScreenConnect devices. Sysdig documented the actor's shift to VShell, an open-source Go RAT published by GitHub user "veo" in 2024, loaded filelessly through SNOWLIGHT.
- Sekoia's configuration-extraction series dissected the ELF loader; Talos covered SNOWLIGHT-delivered VShell under UAT-8302; Google Threat Intelligence Group documented SNOWLIGHT-delivered VShell from actors exploiting React2Shell, associating the ecosystem with UNC6586; EclecticIQ tied it to SAP NetWeaver exploitation; SOCRadar analysed an exposed staging server containing the full toolkit.
- The important correction came from Trend Micro: a SNOWLIGHT-to-VShell intrusion initially assigned to UNC5174 was re-attributed to Earth Lamia on packaging artifacts, with the explicit warning that SNOWLIGHT alone is unreliable for attribution — because it is the default stager the VShell panel generates. Cracked and leaked VShell builds have since spread the framework well beyond its original users. Google describes VShell as publicly available and used by actors with varying motivations.
Our sample's infrastructure fits the commodity end of that ecosystem. The C2 host, 49.232.183.87, is Tencent Cloud (AS45090, block allocated 2010, registered to Tencent Cloud Computing (Beijing)). ThreatFox carried the IP with port 18080 on 2026-09-01. A Joe Sandbox analysis of a larger Windows loader from the same naming series samples arrive as ten random characters plus an extension, with windows_amd64.exe as a recurring internal name recorded bidirectional traffic with this exact IP:port on 2026-09-22, and extracted a VShell configuration:
{"server": "49[.]232.183.87:18080", "type": "tcp", "vkey": "qwe123qwe",
"salt": "qwe123qwe", "proxy": "", "l": false, "e": false, "d": 30, "h": 10}
Authentication key and salt set to qwe123qwe. A panel operator who used a keyboard walk for their credentials is not behaving like an espionage contractor, and we draw no attribution from the tooling. The same infrastructure served Linux ELF droppers in adjacent sandbox analyses the campaign is cross-platform, as the panel's staged-delivery design implies. A sibling loader with the same technique stack (PEB walking, ROR-13 resolution, XOR 0x99) used a different C2 (216[.]126.225.156) over HTTP with a spoofed Firefox user agent, consistent with operators rotating infrastructure per batch.
What we would tell a defender
- The reliable network indicator is the destination pair
49[.]232.183.87:18080, but the more durable detection is the beacon shape: a TCP payload of exactly 40 bytes beginning with"w64 "(or its platform siblings), sent immediately after a connection to a non-standard port, repeated on a ten-second cycle until answered. That rule survives infrastructure rotation. - On the host, the marker file check is a cheap tripwire:
%TEMP%\log_de.logappearing in process-access telemetry around an unsigned small binary is a strong SNOWLIGHT signal and planting the file pre-empts this loader family entirely. - The RWX 30,000,000-byte allocation followed by socket-to-memory streaming is a host-side signature in its own right, and it will not change until the panel's stager generator does.
- Do not attribute on the stager. The architecture tag tells you which platform the operator selected in a panel, not who was using it.
Indicators
C2 (this sample) 49[.]232.183.87:18080/tcp SNOWLIGHT staging + VShell panel
Sibling C2 216[.]126.225.156 same loader series, HTTP variant
Sample SHA-256 e08d62943df1d69416e4a0511a157b1b994577f86a05fb8de4e8f7cb5ece09b3
Sibling SHA-256 ab6677fcbbb1ff4a22cc3e7355e1c36768ba30bbf5cce36f4ec7ae99f850e6c5
Larger loader a5888f690801f422a9da5bfc2604c9a9205a12d10e33741e8a5eef997be52cb3
Imphash e82dd51b077167be63c004bed23d0c1e
Kill switch %TEMP%\log_de.log (presence terminates the Windows stager)
Beacon 40 bytes: "w64 " + port + C2 IP + zero padding
Stage obfuscation single-byte XOR, key 0x99
Sources
- Sysdig Threat Research "UNC5174's evolution in China's ongoing cyber warfare: From SNOWLIGHT to VShell" (2025-04-15)
- Himanshu Anand "A fake resume invoked China's defence-tech elite, then installed VShell" (2026-08-01)
- SOCRadar Threat Research "Tracing SNOWLIGHT: A Government-Targeted Chinese Campaign" (2026-07-31)
- Sekoia "Advent of Configuration Extraction, Part 3: SNOWLIGHT loader" (2025-12-15)
- Malpedia
elf.snowlight,win.rozena(Fraunhofer FKIE) - Joe Sandbox analyses 1975906, 1976046 (sample
a5888f690801f422a9da5bfc2604c9a9205a12d10e33741e8a5eef997be52cb3ELF variants) - OffSeq Threat Radar ThreatFox IOC digest for 2026-09-01
- VirusTotal file
e08d62943df1d69416e4a0511a157b1b994577f86a05fb8de4e8f7cb5ece09b3, IP49.232.183.87, fileab6677fcbbb1ff4a22cc3e7355e1c36768ba30bbf5cce36f4ec7ae99f850e6c5