Skip to content

PoisonX WindowsTelemetry: BYOVD-Assisted RAT With a Plugin Loader

Kirk
10 min read
malwarepoisonxratbyovdreverse-engineeringyara
On this page

On May 19, two WindowsTelemetry archives were linked to a PoisonX tag and a source report from Naoki Takayama / @mopisec (opens in new tab) describing a VERSION.dll sideloading chain, a vulnerable driver step, and C2 at 101.32.190[.]202:8080. Static analysis of both archives produced the same decoded scheduler payload, the same decoded RAT payload, and the same C2. The chain below follows the loader, installer, BYOVD path, and RAT core stage by stage.


Sample overview

Archive SHA256Outer formatTagFirst seen (UTC)
0fb45474ca58bd67220f79b0e3b07f940270c371ba56e27d3e2b99bf4dbb5174WindowsTelemetry.zipPoisonX2026-05-19 04:32
b892981af3ca699d13f07ddcf75c2df62c1543b071278b4cc1ac0993d8b9dc01WindowsTelemetry.rarPoisonX, RustyStealer2026-05-19 06:57

ReversingLabs filed the two archives as Win64.Trojan.Midie and Win64.Trojan.Ravartar respectively; neither name has a Malpedia entry. The working label across both is PoisonX, matching the source report that shipped with them and the MalwareBazaar tag. That label held through analysis: both archives decode to the same scheduler, same RAT binary, and the same C2 at 101.32.190[.]202:8080.

The PoisonX name has public baggage. Maltrail tracks PoisonX Stealer as an alias alongside Ailurophile Stealer and MrAnon Stealer (opens in new tab), with older infrastructure such as poisonx[.]in, poisonx[.]net, 103.116.8[.]66, and 179.43.171[.]201; that same trail now lists this case's C2. Local artifacts contain none of those older domain, IP, Ailurophile, or MrAnon markers, so the name overlap is background, not proof of shared code ancestry. A separate PoisonX.sys / PoisonKiller BYOVD disclosure (opens in new tab) describes a different PID-kill driver using IOCTL 0x22E010; the embedded driver carved from these archives is GLCKIo/WinIo-style through \Device\GLCKIo with IOCTL base 0x80102040, exposing port I/O, physical memory map/unmap, and rdmsr/wrmsr primitives.


Stage 0: DLL sideloading

The outer archive provides:

  • dashost.exe, a legitimate Microsoft binary that imports VERSION.dll.
  • A malicious VERSION.dll placed in the same directory.
  • scheduler.cache and cache.db as encrypted payload artifacts.

When dashost.exe runs, Windows loads the local VERSION.dll through standard DLL search-order hijacking. The malicious VERSION.dll decodes its own embedded strings with XOR 0x7a, then reads scheduler.cache, rolling-XOR-decodes it, manual-maps it into memory, and calls its entrypoint.

Stage 1: The scheduler

The scheduler (decoded PE, 64-bit) acts as installer, persistence mechanism, Defender tamper utility, BYOVD orchestrator, and RAT loader.

Rolling-XOR encoding

Both .cache and .db files use the same encoding scheme:

  • Byte 0: XOR seed
  • Payload starts at offset 1
  • Each payload byte is XORed with (seed + byte_index) & 0xff
ArchiveSeed
0fb0x29
b8920x4a

A second layer of the same rolling XOR is used for the embedded driver blob inside the scheduler. The driver blob (0x4918 bytes at file offset 0x14369) uses seed -10 (0xf6) and produces a valid MZ PE when decoded.

Persistence and installation

The scheduler creates a deployment directory at ProgramData\Microsoft\WindowsTelemetry and copies dashost.exe, VERSION.dll, scheduler.cache, and cache.db into it. It registers services under the names "WinHealthSvc" and "Windows Diagnostics Service", and adds a Run key entry. System-wide and user-level mutexes (Global\SysMtx_51FB4B7B, Global\UsrMtx_EAB7CD0B) prevent multiple instances.

Defender tampering

Before the driver path, the scheduler disables Windows Defender controls:

  • Process and path exclusions for the deployment directory
  • Real-time protection policy values set to disabled
  • MAPS/SpyNet submission policy changed
  • Security Center notification flags disabled
  • PowerShell Add-MpPreference fallback commands for cases where direct registry manipulation fails

BYOVD: embedded driver and callback removal

The scheduler carries an encrypted GLCKIo/WinIo-style vulnerable driver signed with a 2014 compile timestamp (PDB: D:\tmp\GLKIo_git\x64\Win7Debug\Drv.pdb).

Driver propertyValue
Device\Device\GLCKIo
Symlink\DosDevices\GLCKIo
IOCTL base0x80102040
IOCTL surfaceport I/O, physmem map/unmap, rdmsr, wrmsr
Size18,712 bytes

The scheduler:

  1. Disables VulnerableDriverBlocklistEnable under SYSTEM\CurrentControlSet\Control\CI\Config (REG_DWORD 0).
  2. Writes the decrypted driver to disk as an EneTmp* named .sys.
  3. Creates and starts the service via SCM.
  4. Enumerates loaded kernel modules via NtQuerySystemInformation (class 0x0b) and locates the kernel base.
  5. Parses ntoskrnl.exe exports for PsSetCreateProcessNotifyRoutine, PsSetCreateThreadNotifyRoutine, and PsSetLoadImageNotifyRoutine.
  6. Follows relative branch/call targets in the export stubs to find the callback arrays.
  7. Uses the driver's physical memory map IOCTL (0x80102040) to map kernel pages, writes zero over matching callback entries, then unmaps with IOCTL 0x80102044.

The scanner walks up to 0x40 entries per callback array and targets entries whose callback module name matches an embedded list. The effect is that security product kernel callbacks are silently removed, not blocked at the usermode level.

Loading the RAT

After the BYOVD path completes, the scheduler loads cache.db, decodes it with the same rolling-XOR scheme (seed varies by archive), and calls its exported entrypoint (StartPayload).


Stage 2: RAT core

The decoded cache.db is a 64-bit PE exporting StartPayload. It is the command-and-control layer with a custom protocol over raw TCP.

C2

  • 101.32.190[.]202:8080

Passive enrichment showed the IP in Hong Kong (AS132203, Tencent/Aceville infrastructure). Shodan indexed SMB (445), RDP (3389), and WinRM (5985) on the same host but did not see 8080 open. NTLM data from RDP/WinRM points to Windows Server 2019 / Windows 10 build 10.0.17763 hosts. VirusTotal shows 3 malicious and 1 suspicious detections for the IP. GreyNoise, ThreatFox, Feodo, FireHOL, IPsum, and Pulsedive do not currently cluster it as known malicious infrastructure.

Protocol

The 12-byte packet header:

OffsetSizeField
0x004 bytesMagic: 10FX (0x58463031)
0x044 bytesPayload length (little-endian DWORD)
0x084 bytesPacket type (little-endian DWORD)
0x0cvariesPayload

Recovered packet types:

TypeNameBehaviour
0x00Echo/ackSends received body back as type 0
0x02Shell commandCopies payload to string, starts ShellExecThread
0x08Binary inputCalls HandleBinaryInput
0x10JSON task dispatchCalls DispatchJsonTask
0x21Encrypted pluginCalls ParseAndLoadPayload
0x30SOCKS tunnelCalls Socks5HandleTunnelData

Connection lifecycle

StartPayload resolves APIs dynamically, initializes direct syscall helpers, binds Winsock, and loads any cached plugins from disk. It connects to the hardcoded C2, sends a registration JSON block as packet type 1, spawns a heartbeat thread, enters a receive loop, and reconnects after a 5-second sleep on disconnect.

Built-in JSON task surface

The dispatcher (DispatchJsonTask) first checks for a mapped plugin handler. If one exists, the plugin handles the task. If no plugin is mapped, the built-in switch takes over. Plugin payloads were not present in the recovered archives, so the built-in set below is what was confirmed in this build:

TaskFunctionPayload fields
SHELL_EXECcmd.exe /c via ShellExecThreadcommand: string
GET_PROCSProcess enumerationnone
KILL_PROCTerminateProcesspid: number
FREEZE_PROCNtSuspendProcesspid: number
UNFREEZE_PROCNtResumeProcesspid: number
GET_SERVICESEnumServicesStatusnone
CONTROL_SERVICEStart/stop/pause/resumeserviceName, action
GET_WINDOWSEnumWindowsnone
CONTROL_WINDOWClose/minimize/maximize/restore/hide/showhandle, action
GET_SYS_SUMMARYHost fingerprintnone
GET_STARTUP_ITEMSStartup enumerationnone
GET_SOFTWARE_LISTInstalled software (Uninstall key)none
GET_NET_CONNECTIONSGetExtendedTcpTable/UdpTablenone
SWITCH_DISPLAYSelect monitor for screen capturedisplayId: number
START_CAMERAStub - returns "not implemented"none
STOP_CAMERAStub - returns {"ok":true}none
PLUGIN_QUERYLists loaded plugin names and countnone
SOCKS5_STARTInitializes SOCKS relay statenone
SOCKS5_STOPStops SOCKS relay and cleans upnone
SELF_UPDATE_PAYLOADBase64-decode and replace binarydata: base64 string
SELF_RESTARTTriggers restart flownone
CHANGE_SERVER_IPPatches C2 address in cache.dbip, port

Additional task names present in the binary but routed through the plugin system include HDESK_CREATE, HDESK_DESTROY, HDESK_START_STREAM, HDESK_MOUSE, HDESK_KEY, HDESK_RUN, HDESK_SCREENSHOT, START_KEYLOGGER, STOP_KEYLOGGER, GET_KEYLOG_HISTORY, CRYPTO_SWAP_START, CRYPTO_SWAP_STOP, CLEAN_TRACES, WIPE_CREDENTIALS, ELEVATE, GET_DRIVES, GET_DISPLAYS, CLIPBOARD_SET, and TG_ENUM. These require a plugin DLL to be loaded before they function. No plugin payload was recovered in this case.

Screen capture

Screen capture is built into core, not plugin-dependent. It uses GDI/GDI+ to capture the selected display as JPEG. Frames can optionally be compressed with RtlCompressBuffer. The marker byte at frame start is 0 for uncompressed or 1 for compressed, followed by the original uncompressed length DWORD.

SOCKS5 relay

SOCKS tunnelling is multiplexed over the existing RAT C2 connection. The implant maintains a 64-slot tunnel table. Tunnel frames are carried inside packet type 0x30:

Tunnel offsetSizeField
0x004 bytesTunnel ID
0x041 byteCommand: 0x01 connect, 0x02 relay, 0x03 close
0x05variesPayload

For connect commands, the implant supports SOCKS IPv4 (type 0x01) and domain (type 0x03) targets. It calls getaddrinfo, creates a socket with a 10 second timeout, and connects directly from the infected host. No HTTP CONNECT, TLS wrapping, or domain fronting was observed in the relay path.

Plugin format

Encrypted plugins arrive as packet type 0x21 and are parsed by ParseAndLoadPayload:

OffsetField
0Plugin name length (1-31)
1..name_lenPlugin name string
name_len+1XOR key length
next key_lenXOR key
next 4 bytesEncrypted payload length (LE)
remainingEncrypted payload (repeating XOR: payload[i] ^= key[i % key_len])

After decryption the plugin is loaded from memory as a reflective DLL and the decrypted copy is zeroed before freeing.


Detection

A cluster YARA rule covering this chain is available:

The public YARA rule covers four parts of this case: VERSION.dll sideloader artifacts, raw rolling-XOR cache blobs, the decoded BYOVD scheduler PE, and the decoded 10FX RAT core.


IOCs

Network

  • 101.32.190[.]202
  • 101.32.190[.]202:8080

Host artifacts

  • ProgramData\Microsoft\WindowsTelemetry
  • WindowsTelemetry (directory and filename pattern)
  • WinHealthSvc (service name)
  • Windows Diagnostics Service (service name)
  • Global\SysMtx_51FB4B7B (mutex)
  • Global\UsrMtx_EAB7CD0B (mutex)
  • dashost.exe (side-loaded binary)
  • VERSION.dll (sideload payload)
  • scheduler.cache (encrypted stage 1)
  • cache.db (encrypted RAT payload)
  • EneTmp*.sys (written vulnerable driver at runtime)

Registry/behavioural

  • HKLM\SYSTEM\CurrentControlSet\Control\CI\Config\VulnerableDriverBlocklistEnable = 0 (DWORD)
  • Defender exclusion policies for the deployment directory
  • Real-time protection disable values under Windows Defender policy
  • Security Center notification suppression
  • \Device\GLCKIo (driver device object)
  • \DosDevices\GLCKIo (driver symlink)

Protocol

  • 10FX packet magic at TCP layer
  • 12-byte header: magic DWORD, length DWORD, type DWORD
  • Rolling XOR encoding with per-file seed byte

Hashes (all SHA256)

  • 0fb VERSION.dll: 62431e499db7c6a02e93c5f9c79fbcff954144db1b016695d3f34f30c89d0b44
  • b892 VERSION.dll: 0ea1335fefc490622dae07b1a5936a539fa4152f89b64f4b270c8e23846deba6
  • Decoded scheduler: c07573810f5f4578315681ca9108ada8a56eefc1b4786b4e93b54b7abf4b028c
  • Decoded RAT: 0f841b7bddf9788589fce191bb3e7f9f52ec76adb67ff8c360618df8745ee320
  • Embedded driver: 38c18db050b0b2b07f657c03db1c9595febae0319c746c3eede677e21cd238b0
Share this article