PureLogs: Reverse Engineering a .NET RAT From the PureCoder Ecosystem
We recovered a ConfuserEx-protected .NET assembly from a multi-stage intrusion. At the time of recovery it had zero detections on VirusTotal and zero public intelligence. No OSINT, no vendor reports. After first submission, 26 of 72 engines flagged it as trojan.tedy/msil, a generic label with no family attribution. As of 2026-02-26, the sample is 37 of 76 with the same generic naming and no family-level label. Everything below comes from static analysis and string decryption of the binary itself.
Update: Subsequent investigation identified this sample as PureLogs (internally "Plog"), a component of the PureCoder malware-as-a-service ecosystem. The campaign has been publicly tracked by Securonix as SERPENTINE#CLOUD, with additional reporting from Check Point Research, Fortinet, and eSentire. The C2 infrastructure is shared with other PureCoder tools including PureHVNC. The original analysis below remains unchanged. Attribution details are in the attribution section.
Tria.ge refresh (2026-02-26)
A Tria.ge refresh was run across seven campaign artifacts recovered during this case. Results:
| Artifact | SHA256 | Tria.ge ID | Score | Family | Key signatures |
|---|---|---|---|---|---|
| Mvfsxog.dll (core DLL) | 046d0e83c1e6dcaf526127b81b962042e495f5ae3a748f3a9452be62f905acf8 | 260226-mszn4sbx2b | 3 | - | Unsigned PE |
| Qdjlj.dll (Oct05 inner assembly) | cdf87d68885caa3e94713ded9dd5e51c39b7bc7ef9bf7d63a4ff5ab917a96b36 | 260226-mszzwabx2c | 3 | - | Unsigned PE |
| Fviwknzr.exe (Oct05 outer loader) | dcd22d338a0bc4cf615d126b84dfcda149a34cf18bc43b85f16142dfb019e608 | 260226-na81hacs3e | 7 | - | Unsigned PE, AddClipboardFormatListener, AdjustPrivilegeToken, SetWindowsHookEx |
| Nov19 extracted assembly | 60bea9b2f30f77785cc1c12a0436123330a36847ac427a3accf73da02241b04b | 260226-ms1amsbx2d | 10 | donutloader | Detects DonutLoader, Donutloader family, NtCreateUserProcessOtherParentProcess |
| Nov19 extracted .NET payload | 7040fe9721b7ed070f84c47280b6659aaec1eea0fa110af74ba2093d6e20d237 | 260226-ms1leabx2e | 10 | donutloader | Detects DonutLoader, Donutloader family |
| Nov10 campaign executable | 0ab09a4787ea9cb259cadd3f811a56f7bd0058287634bbaf0388b2cd40464505 | 260226-mt3gdsbx4g | 3 | - | Unsigned PE, AdjustPrivilegeToken |
| Nov19 campaign executable | b1c6659ee4ee35540f5ed043b611ac88a7fce9dc2f564168e7d47c43683163f6 | 260226-mt3r6abx4h | 3 | - | Unsigned PE, AdjustPrivilegeToken |
The two Nov19 extracted artifacts were classified as donutloader at score 10. The core PureLogs DLL (Mvfsxog.dll) and Oct05 fat client (Qdjlj.dll) remained unclassified in this refresh run (score 3, unsigned-PE signatures only). The Oct05 outer loader (Fviwknzr.exe) scored 7 with loader-stage behavioural signatures. This is also the same hash reused in the Nov19 phvnc slot. In this batch, Tria.ge family attribution tracked loader-stage behaviour, not the inner PureLogs family name used in this report.
Sample overview
| Field | Value |
|---|---|
| Internal name | Mvfsxog.dll |
| SHA256 | 046d0e83c1e6dcaf526127b81b962042e495f5ae3a748f3a9452be62f905acf8 |
| MD5 | 27c8f921a28bea1f8ee73aaae49b8ddf |
| Type | .NET assembly (DLL) |
| Framework | .NET Framework 4.x |
| Obfuscator | ConfuserEx v1.x (standard variant) |
| VT detections | 0/72 at recovery, 26/72 after first submission, 37/76 as of 2026-02-26 (trojan.tedy/msil) |
| Family | PureLogs (PureCoder MaaS ecosystem) |
| Campaign | SERPENTINE#CLOUD (Securonix) |
The assembly is the inner payload of a multi-stage loader chain. An outer crypter handles decryption and decompression before loading this DLL via Assembly.Load(). Python-based droppers handle delivery and persistence upstream. This DLL is the final stage.
Architecture: plugin-based stager
This is not a monolithic RAT. The DLL has no offensive capabilities. No keylogger. No screen capture. No file manager. No remote shell. No credential harvester. No persistence mechanism.
It is a stager. It does four things:
- Evade analysis environments
- Fingerprint the host
- Open an encrypted C2 channel
- Receive and execute .NET assemblies in memory
All offensive capabilities come from the C2 server at runtime as compiled .NET plugins. They load via Assembly.Load(byte[]), execute via reflection, and never touch disk. This is why it had zero detections at recovery - there is nothing malicious in the binary itself for signature engines to match.
Execution flow
Assembly Load
-> Module static constructors (x4)
-> Anti-tamper init (JIT compilation hook)
-> String decryption table init
-> Entry point
-> Decrypt embedded config (protobuf binary, base64-encoded)
-> Deserialize config into 12-field structure
-> Set SecurityProtocolType.Tls12
-> Mutex check (disabled in this build; config field is null)
-> Sandbox/VM detection
-> If detected: Environment.Exit(0)
-> C2 connection loop
-> TCP connect to C2 server
-> Handshake: send 0x04, expect 0x04
-> Start heartbeat timer (20-40 unit intervals)
-> Enter receive loop:
-> Receive 4-byte length prefix
-> Receive encrypted payload
-> Decrypt (AES-256-CBC)
-> Decompress (GZip)
-> Deserialize (protobuf)
-> Extract plugin assembly bytes
-> Assembly.Load(bytes)
-> GetTypes()[0].GetMethods()[0]
-> InvokeMember(BindingFlags.InvokeMethod)
-> On error: disconnect, sleep 1000-5000ms (random), retry
Anti-analysis
The DLL runs five checks before it ever connects to C2.
Sandbox detection
The sandbox check evaluates multiple signals and exits silently if any match.
DLL presence checks:
SbieDll.dll(Sandboxie)cuckoomon.dll(Cuckoo Sandbox)vmGuestLib.dll(VMware Guest Additions)
WMI queries:
Win32_VideoController- regex match forVMware|VIRTUAL|A M I|XenWin32_DiskDrive- regex match for virtual disk vendor stringsWin32_ComputerSystem- BIOS fingerprintingWin32_Process- parent process name check against known sandbox launchers
Screen resolution thresholds: checks against 768, 900, 1024, 1280, and 1440 pixel widths. Standard VM display sizes trigger detection.
Username blacklist:
johnannaxxxxxxxx
These are default usernames in common automated analysis sandboxes.
System file check: looks for a specific file under Environment.SpecialFolder.System, likely a sandbox artifact.
ConfuserEx protections
Control flow flattening: every method in the assembly uses while(true) { switch(num) { case N: ... } } dispatch. About 120 opaque predicate fields (all initialized to zero) are referenced in branch conditions throughout the code to break static analysis tools.
String encryption: all strings are encrypted at rest and decrypted at runtime via a custom stream cipher. The cipher uses an 8x uint32 key with cyclic key usage and state propagation. 513 strings total.
Anti-tamper / JIT hook: the module initializer installs a JIT compilation hook via native API delegates (VirtualAlloc, WriteProcessMemory, VirtualProtect). Method bodies are stored encrypted in an embedded resource and decrypted at JIT time. Hash-based integrity verification prevents patching.
StackFrame caller check: the string decryption method verifies its call stack using System.Diagnostics.StackFrame. A counter field increments on each call; the check is bypassed only after the counter passes 75. This prevents direct reflection invocation of the decryption method without first understanding the bypass.
C2 protocol
Transport
Raw TCP socket over IPv4. No TLS at the transport layer. Encryption is application-layer.
Framing
4-byte little-endian length prefix followed by the encrypted payload. Max message size: 5,242,880 bytes (5 MB).
Encryption
AES-256-CBC with a hardcoded 32-byte key:
{82, 102, 104, 110, 32, 77, 24, 34,
118, 181, 51, 17, 18, 51, 12, 109,
10, 32, 77, 24, 34, 158, 161, 41,
97, 28, 118, 181, 5, 25, 1, 88}
IV is computed at runtime with arithmetic obfuscation. Provider is AesCryptoServiceProvider with RijndaelManaged fallback. A custom MD5 implementation is used when FIPS policy prevents MD5CryptoServiceProvider.
Compression
GZip with a 4-byte length prefix (decompressed size prepended to the compressed stream).
Serialization
Protocol Buffers via protobuf-net. The full protobuf-net library (~480 classes) is embedded in the assembly.
Handshake
Single-byte exchange. The client sends 0x04. The server responds with 0x04. No certificate exchange, no challenge-response. Connection authentication relies on knowledge of the C2 address and the AES key.
Heartbeat
A timer-based keepalive sends an empty heartbeat protobuf message (type 18) at random intervals between 20 and 40 time units.
Reconnection
On connection failure, the client disconnects cleanly (SocketShutdown.Both), sleeps for a random 1000-5000ms, then retries. The loop runs forever.
Protobuf message protocol
All messages inherit from a single base class with 24 [ProtoInclude] subtypes. The type hierarchy is defined at compile time via attributes, not registered at runtime.
| # | Fields | Purpose |
|---|---|---|
| 1 | 12 lists of subtypes, byte[], string | Aggregate data container |
| 2 | 16 strings, byte[], bool, long | System fingerprint (20 fields) |
| 3 | 3 strings | Key-value-extra triple |
| 4 | 2 strings (Name, Value) | Named key-value pair |
| 5 | 5 strings | Named entity with metadata |
| 6 | 2 strings, 1 int | Status/progress message |
| 7 | 2 strings | Simple key-value |
| 8 | 3 strings, byte[] | Resource data with binary payload |
| 9 | string, long, byte[], 2 bools | Chunked transfer (path, offset, data, flags) |
| 10 | long, sysinfo, bool | System info wrapper with ID |
| 11 | string, byte[], list, string | Named binary data with path list |
| 12 | long, list, byte[], sysinfo | File operation data |
| 13 | string, byte[], string | Named binary payload |
| 14 | bool | Boolean acknowledgment |
| 15 | 3 strings | Named entity |
| 16 | 5 strings, list | Group/collection with children |
| 17 | list, sysinfo | Collection report with system info |
| 18 | (empty) | Heartbeat/keepalive ping |
| 19 | 5 lists, sysinfo | Bulk response container |
| 20 | 1 string | Simple string message |
| 21 | list of strings | String list (arguments/paths) |
| 22 | byte[], data, string, list | Command wrapper (plugin bytes + context) |
| 23 | bool, string, int, 2 strings, bool, string, 4 bools, command | Configuration |
| 24 | 1 string | Simple string message |
Message type 22 is the command wrapper. Its first field carries the compiled .NET assembly bytes. The handler decrypts, decompresses, calls Assembly.Load(), and invokes the first method on the first exported type.
Configuration
The config is a 12-field protobuf message embedded in the assembly as a base64-encoded, encrypted blob. Decoded:
| # | Type | Field | Extracted value |
|---|---|---|---|
| 1 | bool | Feature flag | (sandbox check toggle) |
| 2 | string | C2 host | ydspwie.duckdns.org |
| 3 | int | C2 port | 9045 |
| 4 | string | Encryption key | hyPfc/rn9B2SRQX5h45RMjojzyLIgIY9 |
| 5 | string | Group | Default |
| 6 | bool | Feature flag | |
| 7 | string | Mutex name | (unused, null in config) |
| 8 | bool | Feature flag | |
| 9 | bool | Feature flag | |
| 10 | bool | Feature flag | |
| 11 | bool | Feature flag | |
| 12 | command | Embedded initial plugin | Auto-execute on first connect |
Field 12 is interesting. It carries a command wrapper (type 22) inside the config itself. The operator can bake in a plugin that runs automatically on first C2 connection, before the server sends anything.
System fingerprint
On connection, the RAT sends a 20-field system profile:
| # | Type | Likely content |
|---|---|---|
| 1 | string | Hostname |
| 2 | string | Username |
| 3 | string | OS version |
| 4 | string | Architecture |
| 5 | string | Domain |
| 6 | string | IP address |
| 7 | string | CPU info |
| 8 | string | GPU info |
| 9 | string | RAM info |
| 10 | string | Disk info |
| 11 | string | .NET version |
| 12 | string | Antivirus product |
| 13 | string | Installed programs / running processes |
| 14 | string | Webcam info |
| 15 | string | Firewall status |
| 16 | string | Geographic location / language |
| 17 | byte[] | Screenshot thumbnail or icon |
| 18 | string | Unique bot ID (HWID) |
| 19 | bool | Admin privilege flag |
| 20 | long | Timestamp or uptime |
Plugin execution
The command handler shows the full plugin lifecycle:
-
C2 server sends a command wrapper message (type 22) containing:
- Plugin bytes: compiled .NET assembly
- Context data (file lists, arguments, system info reference)
-
The RAT processes the command:
// Deobfuscated command handler:
byte[] pluginBytes = Decrypt(Decompress(command.PluginBytes));
Assembly asm = Assembly.Load(pluginBytes);
Type entryType = asm.GetTypes()[0];
MethodInfo entryMethod = entryType.GetMethods()[0];
entryType.InvokeMember(
entryMethod.Name,
BindingFlags.InvokeMethod,
null, null,
new object[] { serializedContext }
);
- The plugin receives serialized context (config, connection state) and runs entirely in memory.
Plugins never touch disk. No file artifacts. The stager itself has no signatures because the offensive tools live on the server. Any .NET code the operator compiles can be pushed. And static analysis of the stager alone tells you nothing about what the operator actually did on the box.
Attribution
This sample is PureLogs, a component of the PureCoder malware-as-a-service ecosystem. The name "Plog" appears consistently in dropper filenames throughout the campaign: Ploggggggg, 1plog-obf.py, OBKSLazyNov20_plog.py. PureLogs is the full name; Plog is the operator's shorthand.
PureCoder is a MaaS operator selling a suite of .NET RATs. The toolkit includes PureLogs, PureHVNC, and repackaged commodity RATs (AsyncRAT, VenomRAT, DcRat, XWorm). The campaign was first publicly documented by Securonix in June 2025 under the codename SERPENTINE#CLOUD.
Infrastructure overlap
The C2 IP 45.58.143.254 (Sharktech, Amsterdam) hosts multiple PureCoder tools simultaneously:
| RAT | Domain | Port |
|---|---|---|
| PureLogs (this sample) | ydspwie.duckdns.org | 9045 |
| PureHVNC / PureLogs (Oct05 variant) | nhvncpure.duckdns.org + variants | 6757-6759, 8075-8076 |
| Violet RAT v4.7 | vijdklet.duckdns.org | 7575 |
The nhvncpure* domain naming convention decodes to "PureHVNC" - the domains are shared SERPENTINE#CLOUD IOCs confirmed by Securonix.
Vendor reporting
| Vendor | Report | Coverage |
|---|---|---|
| Securonix | Analyzing SERPENTINE#CLOUD | Campaign tracking, IOCs, delivery chain |
| Check Point Research | Under the Pure Curtain | PureCoder developer analysis, UTC+0300 timezone attribution |
| Fortinet | PureHVNC Deployed via Python Multi-stage Loader | Loader chain, PureHVNC component |
| eSentire | Quartet of Trouble | PureLogs delivery via TryCloudflare tunnels |
Oct05 fat client (Qdjlj.dll)
The campaign deployed two distinct PureLogs builds. The analysis above covers Mvfsxog.dll -- the plugin stager. This section covers the other variant.
The Oct05 variant is a monolithic RAT with all capabilities compiled in. No plugins, no runtime downloads. Everything the operator needs is in the binary.
| Field | Value |
|---|---|
| Internal name | Qdjlj.dll |
| SHA256 | cdf87d68885caa3e94713ded9dd5e51c39b7bc7ef9bf7d63a4ff5ab917a96b36 |
| Size | 1,290,752 bytes |
| Type | .NET assembly (DLL) |
| Framework | .NET Framework 4.0 |
| Obfuscator | ConfuserEx (control flow flattening + string encryption + delegate indirection) |
| ProtoInclude types | 86 (vs 24 for the stager) |
| Source files | 1,747 C# files (decompiled) |
| Version | 4.3.0 |
| Campaign ID | SEP04 |
| Mutex | 3ddc38f1ccff (15-second timeout) |
The inner Qdjlj.dll sample (cdf87d68885caa3e94713ded9dd5e51c39b7bc7ef9bf7d63a4ff5ab917a96b36) first appeared on VirusTotal on 2026-02-26 during this refresh run and currently scores 17/76 with generic detections. The outer Oct05 loader (dcd22d338a0bc4cf615d126b84dfcda149a34cf18bc43b85f16142dfb019e608, Fviwknzr.exe) has been on VirusTotal since 2025-09-18 and currently scores 49/76. That same loader hash is reused in the Nov19 phvnc slot.
Oct05 C2 transport
The two variants handle encryption at different layers. Mvfsxog.dll uses raw TCP with AES-256-CBC at the application layer. The Oct05 variant uses TLS at the transport layer and has no application-layer crypto at all.
| Parameter | Oct05 (Qdjlj.dll) | Stager (Mvfsxog.dll) |
|---|---|---|
| Transport | TCP + TLS 1.0 (SslProtocols.Tls) | Raw TCP |
| Encryption | TLS (transport layer) | AES-256-CBC (application layer) |
| Certificate | Self-signed, CN=Zwfweayg, RSA 4096-bit | None (no TLS) |
| Cert validation | None (callback returns true) | N/A |
| Framing | Length-prefixed protobuf | 4-byte little-endian length prefix |
| Read buffer | 512 KB | 5,242,880 bytes (5 MB max message) |
| Send buffer | 64 KB | -- |
| Timeouts | 5.0 seconds (read/write) | -- |
| Reconnect delay | 5,000 ms | 1,000-5,000 ms (random) |
| Heartbeat | 20-60 second random interval | 20-40 unit intervals |
| Handshake | TLS handshake (standard) | Single-byte: 0x04 send, 0x04 receive |
The embedded TLS certificate:
| Field | Value |
|---|---|
| Subject CN | Zwfweayg |
| Issuer | Self-signed |
| Issued | 2024-06-14 06:44:12 UTC |
| Expires | 9999-12-31 23:59:59 |
| Key | RSA 4096-bit |
| SHA256 | b1474a16875185cc69f41c8b545f591f4bc8a1c7cbdd1cb08924424d825cb6c0 |
The certificate is base64-encoded in the protobuf config (1,676 characters). The operator generated it a year before deployment.
Oct05 C2 infrastructure
The Oct05 variant has 10 C2 domains across 5 ports. The stager has one domain and one port.
| Domain | IP (Feb 2026) | Status |
|---|---|---|
nhvncpure.duckdns.org | 45.58.143.254 | Active (Sharktech Amsterdam) |
nhvncpurekfl.duckdns.org | 45.58.143.254 | Active |
nhvncpureybs.duckdns.org | 45.58.143.254 | Active |
nhvncpureybv.duckdns.org | 85.208.84.113 | Dormant (DNS active, all ports closed) |
nhvncpure.click | NXDOMAIN | Expired |
nhvncpure.shop | NXDOMAIN | Expired |
nhvncpure.sbs | NXDOMAIN | Expired |
nhvncpure.twilightparadox.com | NXDOMAIN | Removed by provider |
nhvncpure1.strangled.net | NXDOMAIN | Removed by provider |
nhvncpure2.mooo.com | NXDOMAIN | Removed by provider |
All 10 domains listen on ports 6757, 6758, 6759, 8075, and 8076. Three active DuckDNS domains resolve to 45.58.143.254 -- the same Sharktech Amsterdam IP hosting the stager (ydspwie:9045) and Violet RAT (vijdklet:7575).
One domain (nhvncpureybv) points to 85.208.84.113 (Pfcloud AS51396 / Online Connect Ltd / NELEEL, Russian allocation December 2025). As of February 2026 this IP is dormant -- DNS resolves but all C2 ports are closed. Standby infrastructure.
The DNS provider spread (DuckDNS, .click, .shop, .sbs, strangled.net, mooo.com, twilightparadox.com) mirrors the Remcos homoney infrastructure analyzed in the AutoIt persistence post -- identical fallback providers.
Oct05 configuration
The config is an 18-field protobuf message -- more complex than the stager's 12-field structure. Decoded from a base64-encoded, GZip-compressed protobuf blob in the application string table.
| Field | Type | Value | Purpose |
|---|---|---|---|
| 1 | repeated string | 10 C2 domains | Server pool |
| 2 | repeated varint | 6759, 6758, 6757, 8075, 8076 | C2 ports |
| 3 | string | Base64 X.509 certificate (1,676 chars) | TLS cert (CN=Zwfweayg) |
| 4 | string | SEP04 | Campaign ID |
| 6 | varint | 1 | SSL enabled |
| 8 | string | APPDATA | Install directory (%AppData%) |
| 9 | string | 3ddc38f1ccff | Mutex |
The remaining fields cover beacon intervals, registry keys, fallback C2 endpoints, and module paths -- all runtime-decrypted from the ConfuserEx string table.
Oct05 capabilities
The binary's 1,747 source files cover the full tool suite.
Cryptocurrency stealer. The primary payload. Targets 50+ browser wallet extensions by Chromium extension ID, 30+ browser profile paths, and 10+ desktop wallet applications.
Browser wallet targets include MetaMask, Phantom, Coinbase Wallet, Trust Wallet, Exodus Web3, Keplr, Ronin, TronLink, Guarda, XDEFI, Binance Chain, Coin98, Math Wallet, Yoroi, BitKeep, and 35 more.
Targeted browsers: Chrome, Edge, Brave, Vivaldi, Opera, CocCoc, Amigo, Torch, Kometa, Orbitum, CentBrowser, 7Star, Sputnik, Chromodo, Chedot, QIP Surf, liebao, Uran, Epic Privacy, Comodo Dragon, K-Melon, Maxthon3, Nichrome, 360Browser, QQBrowser, ChromePlus, Iridium, Coowon, Citrio, Sleipnir5, Atom.
Desktop wallets: Bitcoin-Qt, Dash-Qt, Litecoin-Qt, Electrum, Ethereum (keystore), Atomic Wallet, Exodus, Jaxx Liberty, Ledger Live, Zcash.
Telegram and Foxmail theft. Targets the Telegram Desktop directory and Telegram.exe process for session data. Also harvests Foxmail email client data.
Process injection. Dynamic API resolution pipeline to avoid static import table entries:
LoadLibrary("kernel32.dll")
-> GetProcAddress("OpenProcess")
-> GetProcAddress("VirtualAlloc")
-> GetProcAddress("WriteProcessMemory")
-> GetProcAddress("VirtualProtect")
-> GetProcAddress("CloseHandle")
Screen capture. Graphics.CopyFromScreen at 640x480 resolution, JPEG encoding with variable quality, using SRCCOPY + CAPTUREBLT raster operations.
Persistence. PowerShell scheduled task with 5-minute repeat interval:
Register-ScheduledTask -TaskName '<name>'
-Action (New-ScheduledTaskAction -Execute '<path>')
-Trigger (New-ScheduledTaskTrigger -Once -At (Get-Date)
-RepetitionInterval (New-TimeSpan -Minutes 5))
-User $env:UserName -RunLevel Highest -Force
Executed via powershell.exe -NoProfile -ExecutionPolicy Bypass -Enc <base64>. A second variant omits -RunLevel Highest for lower-privilege persistence.
WMI fingerprinting. Six queries for host profiling:
| Query | Purpose |
|---|---|
Win32_PnPEntity WHERE PNPClass = 'Image' OR PNPClass = 'Camera' | Camera detection |
AntiVirusProduct (namespace: root\\SecurityCenter2) | AV enumeration |
Win32_OperatingSystem | OS version |
Win32_Processor / ProcessorId | CPU fingerprint |
Win32_DiskDrive / SerialNumber | Disk serial |
Win32_PhysicalMemory / SerialNumber | Memory serial |
Other capabilities include window title monitoring (GetForegroundWindow + GetWindowText), anti-idle (SetThreadExecutionState prevents system sleep), DPI awareness (SetProcessDPIAware for accurate screen capture), recursive file enumeration (Parallel.ForEach with wildcard matching), process execution (hidden windows), and registry Shell Folders enumeration.
What's absent: No SetWindowsHookEx, no WH_KEYBOARD, no GetAsyncKeyState -- there is no keylogger. No sandbox evasion, no VM detection, no debugger checks, no environment enumeration. The stager has extensive sandbox detection; this variant has none.
Oct05 string decryption
The Oct05 variant uses a two-tier string encryption architecture, unlike the stager's single table.
| Table | Resource | Strings | Method | Content |
|---|---|---|---|---|
| Library | AL2OceSywAAjnWdFqK (24,952 B) | 506 | IL opcode emulation | protobuf-net serialization framework |
| Application | yAmFdOUdhZalvLPjJS (15,891 B) | 236 | .NET reflection invoke | C2 config, wallet IDs, browser paths, persistence commands |
The library strings are protobuf-net framework internals -- wire-type handlers, serialization callbacks, schema generation strings. No operational content. They were cracked by emulating the ConfuserEx key builder's 2,876 IL instructions to recover the 32-byte cipher key.
The application strings are where everything operational lives. They were cracked by loading the assembly and invoking its own decryption method via .NET reflection (n7V5ZJSQosy6s1ljW6f.x9dSlJCWDO). The 236 decrypted strings yielded all 10 C2 domains, all 5 ports, the TLS certificate, the campaign ID, and the mutex. They also contained 50+ crypto wallet extension IDs, 30+ browser profile paths, desktop wallet targets, Telegram/Foxmail targeting strings, PowerShell persistence commands, and WMI fingerprinting queries.
By comparison, the stager stores all 513 strings in a single table cracked by the same reflection technique. The stager's strings are mostly sandbox detection indicators and protobuf-net framework strings, with one base64-encoded config blob containing the C2 endpoint.
Variant comparison
| Feature | Oct05 fat client (Qdjlj.dll) | Plugin stager (Mvfsxog.dll) |
|---|---|---|
| Architecture | Monolithic -- all capabilities compiled in | Stager -- capabilities delivered via C2 plugins |
| ProtoInclude types | 86 | 24 |
| Target framework | .NET Framework 4.0 | .NET Framework 4.5 |
| Config fields | 18 ProtoMember | 12 ProtoMember |
| C2 transport | TCP + TLS 1.0 (self-signed cert) | Raw TCP + AES-256-CBC (application layer) |
| C2 infrastructure | 10 domains / 5 ports | 1 domain / 1 port |
| Sandbox detection | None | Extensive (DLL checks, WMI, screen resolution, username blacklist) |
| Built-in capabilities | Crypto stealer, Telegram/Foxmail theft, process injection, screen capture, persistence, WMI fingerprinting | None -- all via in-memory plugins |
| String tables | Two-tier: 506 library + 236 application | Single: 513 strings |
| Campaign ID | SEP04 | Default |
| Mutex | 3ddc38f1ccff (active, 15s timeout) | Disabled (config field null) |
| Version | 4.3.0 | Unknown |
| Deployment | Wave 1 (Oct 5) through Wave 6 (Dec 17) | Wave 2 (Nov 10) through Wave 5 (Nov 19) |
The fat client is the older variant. First deployed in October 2025 (campaign ID SEP04, PE timestamp September 4, 2025), it packs everything the operator needs into a single binary. The stager is newer -- stripped down, plugin-based, harder to detect. Moving from fat client to plugin stager means offensive capabilities leave disk entirely. They arrive through the C2 channel, run in memory, and leave no static signatures.
Oct05 indicators of compromise
Network
| Indicator | Type | Context |
|---|---|---|
nhvncpure.duckdns.org | Domain | Oct05 C2 (active) |
nhvncpurekfl.duckdns.org | Domain | Oct05 C2 (active) |
nhvncpureybs.duckdns.org | Domain | Oct05 C2 (active) |
nhvncpureybv.duckdns.org | Domain | Oct05 C2 (dormant -- 85.208.84.113) |
nhvncpure.click | Domain | Oct05 C2 (expired) |
nhvncpure.shop | Domain | Oct05 C2 (expired) |
nhvncpure.sbs | Domain | Oct05 C2 (expired) |
nhvncpure.twilightparadox.com | Domain | Oct05 C2 (removed) |
nhvncpure1.strangled.net | Domain | Oct05 C2 (removed) |
nhvncpure2.mooo.com | Domain | Oct05 C2 (removed) |
45.58.143.254 | IPv4 | Primary C2 IP (Sharktech, Amsterdam) |
85.208.84.113 | IPv4 | Dormant C2 IP (Pfcloud AS51396 / NELEEL) |
6757/tcp, 6758/tcp, 6759/tcp, 8075/tcp, 8076/tcp | Ports | Oct05 C2 ports |
Host
| Indicator | Type | Context |
|---|---|---|
cdf87d68885caa3e94713ded9dd5e51c39b7bc7ef9bf7d63a4ff5ab917a96b36 | SHA256 | Qdjlj.dll (Oct05 inner RAT) |
dcd22d338a0bc4cf615d126b84dfcda149a34cf18bc43b85f16142dfb019e608 | SHA256 | Fviwknzr.exe (Oct05 loader) |
3ddc38f1ccff | Mutex | Oct05 mutex (15s timeout) |
CN=Zwfweayg, RSA 4096-bit | TLS cert | Embedded self-signed certificate |
Behavioral
- TLS 1.0 connection to ports 6757-6759, 8075-8076 with self-signed certificate (CN=Zwfweayg)
- Length-prefixed protobuf framing over TLS
- PowerShell scheduled task creation with 5-minute interval and
-RunLevel Highest powershell.exe -NoProfile -ExecutionPolicy Bypass -Encexecution- Enumeration of Chromium browser extension directories for cryptocurrency wallet extension IDs
- WMI queries to
Win32_PnPEntity,AntiVirusProduct,Win32_OperatingSystem,Win32_Processor,Win32_DiskDrive,Win32_PhysicalMemory - Dynamic API resolution:
LoadLibrary+GetProcAddressfor injection-related APIs - Self-copy to
%AppData%with scheduled task persistence
Encryption suite
The assembly has three independent crypto implementations:
AES-256-CBC for C2 communication. Hardcoded 32-byte key. IV computed with runtime arithmetic obfuscation. Uses AesCryptoServiceProvider with RijndaelManaged fallback.
3DES-ECB for configuration and data encryption. Key derived from MD5 hash of the encryption key config field (field 4).
Custom XOR stream cipher for ConfuserEx string encryption. 8x uint32 key with cyclic key usage and block-level state propagation. Covers all 513 embedded strings.
Indicators of compromise
Network
| Indicator | Type | Context |
|---|---|---|
ydspwie.duckdns.org | Domain | C2 server |
45.58.143.254 | IPv4 | C2 IP (Sharktech, Amsterdam) |
9045/tcp | Port | C2 port |
hyPfc/rn9B2SRQX5h45RMjojzyLIgIY9 | String | C2 encryption key |
Host
| Indicator | Type | Context |
|---|---|---|
046d0e83c1e6dcaf526127b81b962042e495f5ae3a748f3a9452be62f905acf8 | SHA256 | Plog RAT DLL (Mvfsxog.dll) |
27c8f921a28bea1f8ee73aaae49b8ddf | MD5 | Plog RAT DLL (Mvfsxog.dll) |
| Mutex check (code present but disabled) | Behavior | Config field 7 is null; mutex logic not reached |
Behavioral
- Raw TCP connection to port 9045 with 4-byte length-prefix framing
- Single-byte handshake:
0x04send,0x04receive - AES-256-CBC encrypted + GZip compressed payloads over TCP
- WMI queries to
Win32_VideoController,Win32_DiskDrive,Win32_ComputerSystem,Win32_Processat startup - Enumeration of loaded process modules checking for
SbieDll.dll,cuckoomon.dll,vmGuestLib.dll - In-memory .NET assembly loading via
Assembly.Load(byte[])
String decryption
We recovered all 513 encrypted strings by invoking the assembly's own decryption method via .NET reflection. The ConfuserEx stream cipher was bypassed entirely, no key recovery needed. The decryption method's StackFrame caller check was defeated by setting the internal counter field past its threshold (75) via reflection before the first call.
String categories
| Category | Count | Examples |
|---|---|---|
| Anti-sandbox/VM | 16 | SbieDll.dll, cuckoomon.dll, vmGuestLib.dll, VMware|VIRTUAL|A M I|Xen |
| C2 configuration | 1 | Base64-encoded protobuf config blob |
| System interaction | 3 | .dll, Load , whitespace separators |
| Error/debug | 1 | Unexpected WMI query failure |
| Internal identifiers | 2 | GUID fragment 1b24d7e1-0237, obfuscated class reference |
| Plugin loader | ~10 | Assembly loading prefixes and method references |
| protobuf-net library | 476 | Serialization framework strings |
Conclusion
Two PureLogs variants, two architectures, one campaign.
The Oct05 fat client (Qdjlj.dll) is a monolithic crypto stealer -- 86 protobuf message types, 50+ wallet extension targets, built-in persistence, TLS transport, 10 C2 domains across 5 ports. The plugin stager (Mvfsxog.dll) is the opposite -- 24 message types, zero offensive capability, extensive sandbox evasion, a single C2 endpoint. All malicious actions come from plugins pushed in memory that never touch disk.
The shift from fat client to plugin stager is about reducing what touches disk. The fat client leaves signatures: wallet extension IDs, persistence commands, injection APIs. The stager leaves nothing. It had zero VirusTotal detections at recovery because there is nothing malicious in the binary for signature engines to match. After first submission, 26 of 72 engines flagged it with generic labels (trojan.tedy/msil). As of 2026-02-26, it is 37 of 76 and still generic. No engine identified it by family name.
This is PureLogs, sold as part of PureCoder's MaaS toolkit alongside PureHVNC and repackaged commodity RATs. The campaign infrastructure, delivery chain, and dropper naming conventions all tie back to SERPENTINE#CLOUD.
Kirk
I like the internet. Want to get in touch? kirk@derp.ca