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 SHA256 Outer format Tag First seen (UTC)
0fb45474ca58bd67220f79b0e3b07f940270c371ba56e27d3e2b99bf4dbb5174 WindowsTelemetry.zip PoisonX 2026-05-19 04:32
b892981af3ca699d13f07ddcf75c2df62c1543b071278b4cc1ac0993d8b9dc01 WindowsTelemetry.rar PoisonX, RustyStealer 2026-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
Archive Seed
0fb 0x29
b892 0x4a

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 property Value
Device \Device\GLCKIo
Symlink \DosDevices\GLCKIo
IOCTL base 0x80102040
IOCTL surface port I/O, physmem map/unmap, rdmsr, wrmsr
Size 18,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:

Offset Size Field
0x00 4 bytes Magic: 10FX (0x58463031)
0x04 4 bytes Payload length (little-endian DWORD)
0x08 4 bytes Packet type (little-endian DWORD)
0x0c varies Payload

Recovered packet types:

Type Name Behaviour
0x00 Echo/ack Sends received body back as type 0
0x02 Shell command Copies payload to string, starts ShellExecThread
0x08 Binary input Calls HandleBinaryInput
0x10 JSON task dispatch Calls DispatchJsonTask
0x21 Encrypted plugin Calls ParseAndLoadPayload
0x30 SOCKS tunnel Calls 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:

Task Function Payload fields
SHELL_EXEC cmd.exe /c via ShellExecThread command: string
GET_PROCS Process enumeration none
KILL_PROC TerminateProcess pid: number
FREEZE_PROC NtSuspendProcess pid: number
UNFREEZE_PROC NtResumeProcess pid: number
GET_SERVICES EnumServicesStatus none
CONTROL_SERVICE Start/stop/pause/resume serviceName, action
GET_WINDOWS EnumWindows none
CONTROL_WINDOW Close/minimize/maximize/restore/hide/show handle, action
GET_SYS_SUMMARY Host fingerprint none
GET_STARTUP_ITEMS Startup enumeration none
GET_SOFTWARE_LIST Installed software (Uninstall key) none
GET_NET_CONNECTIONS GetExtendedTcpTable/UdpTable none
SWITCH_DISPLAY Select monitor for screen capture displayId: number
START_CAMERA Stub - returns "not implemented" none
STOP_CAMERA Stub - returns {"ok":true} none
PLUGIN_QUERY Lists loaded plugin names and count none
SOCKS5_START Initializes SOCKS relay state none
SOCKS5_STOP Stops SOCKS relay and cleans up none
SELF_UPDATE_PAYLOAD Base64-decode and replace binary data: base64 string
SELF_RESTART Triggers restart flow none
CHANGE_SERVER_IP Patches C2 address in cache.db ip, 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 offset Size Field
0x00 4 bytes Tunnel ID
0x04 1 byte Command: 0x01 connect, 0x02 relay, 0x03 close
0x05 varies Payload

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:

Offset Field
0 Plugin name length (1-31)
1..name_len Plugin name string
name_len+1 XOR key length
next key_len XOR key
next 4 bytes Encrypted payload length (LE)
remaining Encrypted 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