← All posts

PureLogs: Reverse Engineering a .NET RAT From the PureCoder Ecosystem

Kirk24 min read
malwarerat.netincident-responsepurelogspurecoder

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:

ArtifactSHA256Tria.ge IDScoreFamilyKey signatures
Mvfsxog.dll (core DLL)046d0e83c1e6dcaf526127b81b962042e495f5ae3a748f3a9452be62f905acf8260226-mszn4sbx2b3-Unsigned PE
Qdjlj.dll (Oct05 inner assembly)cdf87d68885caa3e94713ded9dd5e51c39b7bc7ef9bf7d63a4ff5ab917a96b36260226-mszzwabx2c3-Unsigned PE
Fviwknzr.exe (Oct05 outer loader)dcd22d338a0bc4cf615d126b84dfcda149a34cf18bc43b85f16142dfb019e608260226-na81hacs3e7-Unsigned PE, AddClipboardFormatListener, AdjustPrivilegeToken, SetWindowsHookEx
Nov19 extracted assembly60bea9b2f30f77785cc1c12a0436123330a36847ac427a3accf73da02241b04b260226-ms1amsbx2d10donutloaderDetects DonutLoader, Donutloader family, NtCreateUserProcessOtherParentProcess
Nov19 extracted .NET payload7040fe9721b7ed070f84c47280b6659aaec1eea0fa110af74ba2093d6e20d237260226-ms1leabx2e10donutloaderDetects DonutLoader, Donutloader family
Nov10 campaign executable0ab09a4787ea9cb259cadd3f811a56f7bd0058287634bbaf0388b2cd40464505260226-mt3gdsbx4g3-Unsigned PE, AdjustPrivilegeToken
Nov19 campaign executableb1c6659ee4ee35540f5ed043b611ac88a7fce9dc2f564168e7d47c43683163f6260226-mt3r6abx4h3-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

FieldValue
Internal nameMvfsxog.dll
SHA256046d0e83c1e6dcaf526127b81b962042e495f5ae3a748f3a9452be62f905acf8
MD527c8f921a28bea1f8ee73aaae49b8ddf
Type.NET assembly (DLL)
Framework.NET Framework 4.x
ObfuscatorConfuserEx v1.x (standard variant)
VT detections0/72 at recovery, 26/72 after first submission, 37/76 as of 2026-02-26 (trojan.tedy/msil)
FamilyPureLogs (PureCoder MaaS ecosystem)
CampaignSERPENTINE#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:

  1. Evade analysis environments
  2. Fingerprint the host
  3. Open an encrypted C2 channel
  4. 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 for VMware|VIRTUAL|A M I|Xen
  • Win32_DiskDrive - regex match for virtual disk vendor strings
  • Win32_ComputerSystem - BIOS fingerprinting
  • Win32_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:

  • john
  • anna
  • xxxxxxxx

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.

#FieldsPurpose
112 lists of subtypes, byte[], stringAggregate data container
216 strings, byte[], bool, longSystem fingerprint (20 fields)
33 stringsKey-value-extra triple
42 strings (Name, Value)Named key-value pair
55 stringsNamed entity with metadata
62 strings, 1 intStatus/progress message
72 stringsSimple key-value
83 strings, byte[]Resource data with binary payload
9string, long, byte[], 2 boolsChunked transfer (path, offset, data, flags)
10long, sysinfo, boolSystem info wrapper with ID
11string, byte[], list, stringNamed binary data with path list
12long, list, byte[], sysinfoFile operation data
13string, byte[], stringNamed binary payload
14boolBoolean acknowledgment
153 stringsNamed entity
165 strings, listGroup/collection with children
17list, sysinfoCollection report with system info
18(empty)Heartbeat/keepalive ping
195 lists, sysinfoBulk response container
201 stringSimple string message
21list of stringsString list (arguments/paths)
22byte[], data, string, listCommand wrapper (plugin bytes + context)
23bool, string, int, 2 strings, bool, string, 4 bools, commandConfiguration
241 stringSimple 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:

#TypeFieldExtracted value
1boolFeature flag(sandbox check toggle)
2stringC2 hostydspwie.duckdns.org
3intC2 port9045
4stringEncryption keyhyPfc/rn9B2SRQX5h45RMjojzyLIgIY9
5stringGroupDefault
6boolFeature flag
7stringMutex name(unused, null in config)
8boolFeature flag
9boolFeature flag
10boolFeature flag
11boolFeature flag
12commandEmbedded initial pluginAuto-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:

#TypeLikely content
1stringHostname
2stringUsername
3stringOS version
4stringArchitecture
5stringDomain
6stringIP address
7stringCPU info
8stringGPU info
9stringRAM info
10stringDisk info
11string.NET version
12stringAntivirus product
13stringInstalled programs / running processes
14stringWebcam info
15stringFirewall status
16stringGeographic location / language
17byte[]Screenshot thumbnail or icon
18stringUnique bot ID (HWID)
19boolAdmin privilege flag
20longTimestamp or uptime

Plugin execution

The command handler shows the full plugin lifecycle:

  1. C2 server sends a command wrapper message (type 22) containing:

    • Plugin bytes: compiled .NET assembly
    • Context data (file lists, arguments, system info reference)
  2. 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 }
);
  1. 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:

RATDomainPort
PureLogs (this sample)ydspwie.duckdns.org9045
PureHVNC / PureLogs (Oct05 variant)nhvncpure.duckdns.org + variants6757-6759, 8075-8076
Violet RAT v4.7vijdklet.duckdns.org7575

The nhvncpure* domain naming convention decodes to "PureHVNC" - the domains are shared SERPENTINE#CLOUD IOCs confirmed by Securonix.

Vendor reporting

VendorReportCoverage
SecuronixAnalyzing SERPENTINE#CLOUDCampaign tracking, IOCs, delivery chain
Check Point ResearchUnder the Pure CurtainPureCoder developer analysis, UTC+0300 timezone attribution
FortinetPureHVNC Deployed via Python Multi-stage LoaderLoader chain, PureHVNC component
eSentireQuartet of TroublePureLogs 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.

FieldValue
Internal nameQdjlj.dll
SHA256cdf87d68885caa3e94713ded9dd5e51c39b7bc7ef9bf7d63a4ff5ab917a96b36
Size1,290,752 bytes
Type.NET assembly (DLL)
Framework.NET Framework 4.0
ObfuscatorConfuserEx (control flow flattening + string encryption + delegate indirection)
ProtoInclude types86 (vs 24 for the stager)
Source files1,747 C# files (decompiled)
Version4.3.0
Campaign IDSEP04
Mutex3ddc38f1ccff (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.

ParameterOct05 (Qdjlj.dll)Stager (Mvfsxog.dll)
TransportTCP + TLS 1.0 (SslProtocols.Tls)Raw TCP
EncryptionTLS (transport layer)AES-256-CBC (application layer)
CertificateSelf-signed, CN=Zwfweayg, RSA 4096-bitNone (no TLS)
Cert validationNone (callback returns true)N/A
FramingLength-prefixed protobuf4-byte little-endian length prefix
Read buffer512 KB5,242,880 bytes (5 MB max message)
Send buffer64 KB--
Timeouts5.0 seconds (read/write)--
Reconnect delay5,000 ms1,000-5,000 ms (random)
Heartbeat20-60 second random interval20-40 unit intervals
HandshakeTLS handshake (standard)Single-byte: 0x04 send, 0x04 receive

The embedded TLS certificate:

FieldValue
Subject CNZwfweayg
IssuerSelf-signed
Issued2024-06-14 06:44:12 UTC
Expires9999-12-31 23:59:59
KeyRSA 4096-bit
SHA256b1474a16875185cc69f41c8b545f591f4bc8a1c7cbdd1cb08924424d825cb6c0

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.

DomainIP (Feb 2026)Status
nhvncpure.duckdns.org45.58.143.254Active (Sharktech Amsterdam)
nhvncpurekfl.duckdns.org45.58.143.254Active
nhvncpureybs.duckdns.org45.58.143.254Active
nhvncpureybv.duckdns.org85.208.84.113Dormant (DNS active, all ports closed)
nhvncpure.clickNXDOMAINExpired
nhvncpure.shopNXDOMAINExpired
nhvncpure.sbsNXDOMAINExpired
nhvncpure.twilightparadox.comNXDOMAINRemoved by provider
nhvncpure1.strangled.netNXDOMAINRemoved by provider
nhvncpure2.mooo.comNXDOMAINRemoved 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.

FieldTypeValuePurpose
1repeated string10 C2 domainsServer pool
2repeated varint6759, 6758, 6757, 8075, 8076C2 ports
3stringBase64 X.509 certificate (1,676 chars)TLS cert (CN=Zwfweayg)
4stringSEP04Campaign ID
6varint1SSL enabled
8stringAPPDATAInstall directory (%AppData%)
9string3ddc38f1ccffMutex

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:

QueryPurpose
Win32_PnPEntity WHERE PNPClass = 'Image' OR PNPClass = 'Camera'Camera detection
AntiVirusProduct (namespace: root\\SecurityCenter2)AV enumeration
Win32_OperatingSystemOS version
Win32_Processor / ProcessorIdCPU fingerprint
Win32_DiskDrive / SerialNumberDisk serial
Win32_PhysicalMemory / SerialNumberMemory 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.

TableResourceStringsMethodContent
LibraryAL2OceSywAAjnWdFqK (24,952 B)506IL opcode emulationprotobuf-net serialization framework
ApplicationyAmFdOUdhZalvLPjJS (15,891 B)236.NET reflection invokeC2 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

FeatureOct05 fat client (Qdjlj.dll)Plugin stager (Mvfsxog.dll)
ArchitectureMonolithic -- all capabilities compiled inStager -- capabilities delivered via C2 plugins
ProtoInclude types8624
Target framework.NET Framework 4.0.NET Framework 4.5
Config fields18 ProtoMember12 ProtoMember
C2 transportTCP + TLS 1.0 (self-signed cert)Raw TCP + AES-256-CBC (application layer)
C2 infrastructure10 domains / 5 ports1 domain / 1 port
Sandbox detectionNoneExtensive (DLL checks, WMI, screen resolution, username blacklist)
Built-in capabilitiesCrypto stealer, Telegram/Foxmail theft, process injection, screen capture, persistence, WMI fingerprintingNone -- all via in-memory plugins
String tablesTwo-tier: 506 library + 236 applicationSingle: 513 strings
Campaign IDSEP04Default
Mutex3ddc38f1ccff (active, 15s timeout)Disabled (config field null)
Version4.3.0Unknown
DeploymentWave 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

IndicatorTypeContext
nhvncpure.duckdns.orgDomainOct05 C2 (active)
nhvncpurekfl.duckdns.orgDomainOct05 C2 (active)
nhvncpureybs.duckdns.orgDomainOct05 C2 (active)
nhvncpureybv.duckdns.orgDomainOct05 C2 (dormant -- 85.208.84.113)
nhvncpure.clickDomainOct05 C2 (expired)
nhvncpure.shopDomainOct05 C2 (expired)
nhvncpure.sbsDomainOct05 C2 (expired)
nhvncpure.twilightparadox.comDomainOct05 C2 (removed)
nhvncpure1.strangled.netDomainOct05 C2 (removed)
nhvncpure2.mooo.comDomainOct05 C2 (removed)
45.58.143.254IPv4Primary C2 IP (Sharktech, Amsterdam)
85.208.84.113IPv4Dormant C2 IP (Pfcloud AS51396 / NELEEL)
6757/tcp, 6758/tcp, 6759/tcp, 8075/tcp, 8076/tcpPortsOct05 C2 ports

Host

IndicatorTypeContext
cdf87d68885caa3e94713ded9dd5e51c39b7bc7ef9bf7d63a4ff5ab917a96b36SHA256Qdjlj.dll (Oct05 inner RAT)
dcd22d338a0bc4cf615d126b84dfcda149a34cf18bc43b85f16142dfb019e608SHA256Fviwknzr.exe (Oct05 loader)
3ddc38f1ccffMutexOct05 mutex (15s timeout)
CN=Zwfweayg, RSA 4096-bitTLS certEmbedded 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 -Enc execution
  • 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 + GetProcAddress for 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

IndicatorTypeContext
ydspwie.duckdns.orgDomainC2 server
45.58.143.254IPv4C2 IP (Sharktech, Amsterdam)
9045/tcpPortC2 port
hyPfc/rn9B2SRQX5h45RMjojzyLIgIY9StringC2 encryption key

Host

IndicatorTypeContext
046d0e83c1e6dcaf526127b81b962042e495f5ae3a748f3a9452be62f905acf8SHA256Plog RAT DLL (Mvfsxog.dll)
27c8f921a28bea1f8ee73aaae49b8ddfMD5Plog RAT DLL (Mvfsxog.dll)
Mutex check (code present but disabled)BehaviorConfig field 7 is null; mutex logic not reached

Behavioral

  • Raw TCP connection to port 9045 with 4-byte length-prefix framing
  • Single-byte handshake: 0x04 send, 0x04 receive
  • AES-256-CBC encrypted + GZip compressed payloads over TCP
  • WMI queries to Win32_VideoController, Win32_DiskDrive, Win32_ComputerSystem, Win32_Process at 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

CategoryCountExamples
Anti-sandbox/VM16SbieDll.dll, cuckoomon.dll, vmGuestLib.dll, VMware|VIRTUAL|A M I|Xen
C2 configuration1Base64-encoded protobuf config blob
System interaction3.dll, Load , whitespace separators
Error/debug1Unexpected WMI query failure
Internal identifiers2GUID fragment 1b24d7e1-0237, obfuscated class reference
Plugin loader~10Assembly loading prefixes and method references
protobuf-net library476Serialization 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.

K

Kirk

I like the internet. Want to get in touch? kirk@derp.ca