← All posts

VioletWorm v4.7 (Violet RAT): The Most Dangerous Payload in a 9-RAT Toolkit

Kirk25 min read
malwarerat.netincident-responseviolet-ratvioletwormxworm

We recovered a .NET assembly from the same multi-stage intrusion that produced PureLogs and PureCrypter. Across six parallel attack chains, the actor deployed nine RAT families. This one is the largest payload, has the widest capability set, and sits on separate infrastructure. The actor's internal label for it was Viooooooo.

Tria.ge classifies this family as violetworm. We use VioletWorm as the canonical name throughout this post and use Violet RAT only where campaign artifacts use that naming.


Sample overview

FieldValue
Familyvioletworm (aka Violet RAT)
Versionv4.7
Original filenameVioletClient.exe
Internal labelViooooooo / vio
SHA256 (Nov10)d656bcfefa98007fcb3e2be4430a9b24d258c046b0c768ac43699436aceb98e6
Size (Nov10)1,432,360 bytes
SHA256 (Nov19)e2c5921e5c354000c38653d05ac3255865d20bc986da34e68246da6f72ec1fed
Size (Nov19)1,425,920 bytes
SHA256 (Nov24)cbd4cd4c42b3ee0bfb99a254a97ff2c2aacc47d83e08601ae8cdf3b333f07806
Size (Nov24)1,029,120 bytes
Type.NET assembly (PE32, Mono)
Framework.NET Framework 4.0
Campaign overlapSERPENTINE#CLOUD (Securonix) -- shared tooling and TTPs, but Securonix reporting does not mention VioletWorm
AssessmentMost dangerous payload in the toolkit

The assembly is the inner payload of a multi-stage loader chain. Python droppers handle delivery. AES-256-CBC or RC4 protects the outer layer. Donut v0.9.2 shellcode handles CLR bootstrap. The final stage runs in memory through reflective assembly loading.

Three builds were recovered. The Nov10 build is the primary analysis target. The Nov19 build is a recompile with the same configuration and command set. The Nov24 build is distinct: different C2 endpoint (vigroup2125.duckdns.org:2125), different XOR keys, and a different mutex. It was recovered from an unobfuscated developer artifact.


Classification and Tria.ge corroboration (2026-02-25)

Tria.ge gives independent classification and runtime support for the three payload assemblies and three loader scripts recovered in this intrusion.

SampleTypeTria.ge IDScoreFamilyStatus
VioletWorm Nov10 payloadpayload260225-st7zrsbz6d10violetwormreported
VioletWorm Nov19 payloadpayload260225-st8ajabz6e10violetwormreported
VioletWorm Nov24 payloadpayload260225-st8w3abz6f10violetwormreported
VioletWorm Nov10 loaderloader260225-syqahsb15d3-reported
VioletWorm Nov19 loaderloader260225-syq7tab15f3-reported
VioletWorm Nov24 loaderloader260225-syrtcab15g3-reported

All three payload submissions were classified as violetworm with score 10 across static and behavioral tasks. Loader submissions still showed behavior but did not receive family attribution. A Tria.ge search for family:violetworm currently shows samples dating back to 2025-11-28T02:07:01Z (251128-cjy14sylcr).

Tria.ge task telemetry corroborated both C2 endpoint sets already recovered from reversing:

  • 45.58.143.254:7575 for Nov10/Nov19 payloads (vijdklet.duckdns.org)
  • Tria.ge runtime endpoint 213.227.152.82:2125 for the Nov24 payload (vigroup2125.duckdns.org); breach-report infrastructure attribution for this domain remains TBD

Tria.ge sample links:


Architecture: massive command dispatcher with C2-delivered plugins

VioletWorm (Violet RAT) is a command dispatcher. The main binary contains an 8,000-line IL handler with 120 decoded command branches. Capabilities such as ransomware, HVNC, keylogging, file management, and credential theft are implemented in plugin DLLs delivered by C2 at runtime. The binary loads those plugins with Assembly.Load(), instantiates Class1, and calls methods through VB.NET late binding (NewLateBinding.LateCall).

This makes it structurally closer to PureLogs than it first appears. Both receive capabilities from C2. The difference is scale. PureLogs has a handful of commands and a small dispatcher. VioletWorm covers remote shell, file manager, keylogger, screenshot streaming, active window monitoring, screen control, HVNC, remote desktop, webcam/mic/audio capture, DDoS, clipboard hijacking, crypto clipping, password stealing, network recon, TCP connection monitoring, Discord grabbing, ransomware, process injection, USB spreading, ngrok tunneling, live chat with the victim, AV enumeration, UAC elevation, and Windows Defender tampering.

At 1.4 MB, it is the largest payload in the toolkit -- 10x larger than AsyncRAT (52 KB) and 3x larger than VenomRAT (70 KB). The bulk is the dispatcher itself and 11 large base64-encoded data blobs stored in the .NET User Strings heap. These blobs are initialized in the config class constructor but never referenced by the main binary's code -- they are likely accessed via reflection by C2-delivered plugins at runtime.


String encoding scheme

All strings in the .NET User Strings (#US) heap are base64 encoded. VioletWorm uses two encoding tiers:

2-layer encoding (plaintext strings): plaintext -> base64 -> base64. These cover configuration fields, command names, registry paths, and file paths. Decoding two layers of base64 yields the plaintext directly.

3-layer encoding (XOR-obfuscated strings): plaintext -> XOR(key) -> base64 -> base64 -> base64. These cover C2 infrastructure, sensitive command identifiers, and User-Agent strings. Three layers of base64 yield XOR-encrypted bytes that require the key to decode.

XOR key derivation

The key is self-contained in the binary. String index [24] in the #US heap stores the key through the same triple-base64 encoding:

Stored:    ZVc1T1JVNUpUQT09
1x base64: eW5ORU5JTA==
2x base64: ynNENIL

Key: ynNENIL (7 bytes: 0x79 0x6e 0x4e 0x45 0x4e 0x49 0x4c)

XOR decryption is byte-wise with key recycling: plaintext[i] = ciphertext[i] ^ key[i % 7].

Verification: the known plaintext uninstall XOR'd with the key matches the bytes stored (after triple-base64) at string index [64]. Once the key is recovered, every XOR-encoded string in the binary decodes immediately.


C2 protocol

Transport

HTTP POST over TCP. No TLS. The C2 address and port are XOR-encoded in the binary.

ParameterValue
C2 domainvijdklet.duckdns.org
C2 port7575
ProtocolHTTP POST
Content-Typeapplication/x-www-form-urlencoded
Field separatorXSXSXSX
KeepalivePING? / PONG

Field separator

All C2 messages use XSXSXSX as the initial field separator in the POST body. Command arguments, file paths, and data chunks are concatenated with this delimiter. The separator is initialized from a 2-layer base64 string in the config, but it is mutable -- the C2 server rotates it mid-session. Since the AES encryption key is derived from MD5(UTF8.GetBytes(separator)), rotating the separator also rotates the encryption key.

Keepalive

The RAT sends PING? at regular intervals. The server responds PONG. If the keepalive fails, the client reconnects.

User-Agent rotation

Six hardcoded User-Agent strings rotate across requests:

#Platform
1iPhone / Safari / iOS
2Windows / Chrome / Edge
3Mac / Safari / macOS
4Linux / Firefox
5iPad / Safari / iPadOS
6Windows / alternate

All six are XOR-encoded (3-layer base64). The rotation makes traffic pattern matching harder -- each request appears to come from a different browser and operating system.

Microsoft URL whitelist

VioletWorm checks outbound URLs against a whitelist of Microsoft domains. If the destination matches any of the following, the request is excluded from interception:

  • windowsupdate
  • winatp-gw-cus
  • watson
  • msedge
  • go.microsoft.com
  • activation.sls

These are telemetry and update endpoints. Whitelisting them prevents the RAT from intercepting legitimate Windows traffic.


Configuration

Extracted fields from the decoded binary:

FieldValueSource
C2 domainvijdklet.duckdns.orgXOR-encoded, index [18]
C2 port7575XOR-encoded, index [19]
VersionViolet v4.7Base64, index [37]
Tag<Violet>Base64, index [21]
XOR keyynNENILBase64, index [24]
Mutex (single-instance)aXTyo1HpFXkKUYoLBase64, index [25]
Mutex GUID #13d847c5c-4f5a-4918-9e07-a96cea49048dBase64, index [43]
Mutex GUID #289c43fcf-5e52-4be7-a719-a26139ce636aBase64, index [50]
USB spreaderUSB.exeBase64, index [22]
Separator (initial)XSXSXSXBase64, index [20]; rotated by C2 at runtime
PasteUrl%PasteUrl% (placeholder, unused)Base64, index [0]
Install filename3d847c5c-4f5a-4918-9e07-a96cea49048d.exeDerived from mutex GUID
Cleanup scriptWinTempClean32.batBase64, index [41]
Tunneling\ngrok.exePath reference

Persistence targets:

  • HKEY_CURRENT_USER\SOFTWARE\ -- Run key persistence
  • HKEY_LOCAL_MACHINE\software\classes\ -- COM/class registration abuse

Three mutex values serve different purposes. aXTyo1HpFXkKUYoL is the primary single-instance lock -- despite looking like an AES-128 key, IL tracing confirms it's only used in new Mutex(false, "aXTyo1HpFXkKUYoL", out bool). The first GUID (3d847c5c...) doubles as the install filename. The second GUID (89c43fcf...) is a secondary instance check.


Capabilities

The dispatcher has 120 command branches, each decoded from double-base64 + XOR-encoded constants in the IL. Below are the ones with enough context to describe. The full command map is in the reference documentation.

Remote shell

CommandFunction
shellfucOpen interactive shell (CMD.EXE)
runnnnnnExecute command in shell
closeshellTerminate shell session

File manager

CommandFunction
showfolderfileList directory contents
creatnewfolderCreate directory
creatfileCreate file
downloadfileExfiltrate file to C2
ExecuteRun file
RenameRename file
viewimageView image file
Delete / DelPDelete file or path
7zIT / 7zzipCompress files with 7zip

The file manager includes a bundled 7zip integration (7zip\7z.exe) for compressing files before exfiltration.

Keylogger

CommandFunction
KLStart keylogger
KLget / KLGETRetrieve captured keystrokes
closeKLStop keylogger

Keylog data is stored under HKEY_CURRENT_USER\SOFTWARE\ -- the same registry path used for persistence.

Screen control and HVNC

CommandFunction
HVNC / HvNcXHidden VNC session (invisible remote desktop)
hvncxdisDisconnect HVNC
shwupDisplay fake "Windows Update" screen overlay
hidupRemove the fake overlay
BSODTrigger Blue Screen of Death

While the victim sees "Installing updates, please wait..." the operator has full control of the hidden desktop.

Webcam, microphone, and audio

CommandFunction
WBCMWebcam capture
MICLMicrophone listener
WsoundSystem audio capture

These three are the only cached plugins. On first use, the C2 sends the plugin DLL and the RAT stores the raw bytes in a static field. Subsequent calls reload from cache instead of re-requesting from the C2.

Screenshot capture

CommandFunction
RSSStart remote screenshot stream
RSSDisStop screenshot stream

Screenshots are captured as JPEG (image/jpeg) with configurable region bounds (X, Y, Width, Height). The stream commands suggest live screen monitoring, not just one-off captures.

Active window monitoring

CommandFunction
ACTStart active window tracking
ACTGGet active window title
killActStop active window tracking

Passive surveillance -- tracks which application the victim is using without capturing keystrokes. The operator can watch for banking sites, email clients, or crypto wallets opening and then trigger other modules (keylogger, screenshot, HVNC) at the right moment.

DDoS

CommandFunction
DDosSStart DDoS attack
DDosTStop DDoS attack

An unusual capability for a RAT deployed in a targeted intrusion. The infected machines can be enrolled as DDoS nodes on demand.

Clipboard and crypto clipper

CommandFunction
CilpperEnable clipboard monitor / crypto clipper
clssClear clipboard

The crypto clipper monitors the clipboard for cryptocurrency wallet addresses and silently replaces them with attacker-controlled addresses. The dispatch command is misspelled -- Cilpper instead of Clipper. A correctly-spelled copy exists at string index [104], but the dispatcher uses the XOR-encoded misspelling at index [103].

Network reconnaissance and sniffer

CommandFunction
NetDisCVNetwork discovery scan
SnifStrtStart network sniffer
SniffKllStop sniffer
TCPVView active TCP connections
TCPGGet TCP connection details

Ransomware

CommandFunctionPlugin methodArgs
ENCEncrypt target fileENC(hwid, path)2
DECDecrypt target fileDEC(hwid, path)2
RENCRecursive directory encryptionENC(hwid, ...params)6 (5 ByRef)
RDECRecursive directory decryptionDEC(hwid)1

All four commands are confirmed in the IL command dispatcher. Like every other capability, the actual encryption logic lives in a C2-delivered plugin DLL.

The first argument to every call is a machine fingerprint: MD5(ProcessorCount + UserName + MachineName + OSVersion + DriveSize), output as a 32-character lowercase hex string. This HWID is the primary cryptographic input to the plugin -- RDEC receives it as its only argument, meaning the plugin needs nothing else from the RAT to reverse the encryption. The actual encryption algorithm and key derivation are inside the C2-delivered plugin DLL, which we do not have.

ENC and DEC are single-file operations: the C2 sends a plugin DLL and a target path. RENC and RDEC operate on directory trees and are governed by a state machine. A guard flag prevents concurrent execution:

  1. RENC arrives -- flag set to 1 (encrypting), plugin loaded, ENC() called with 6 arguments (5 ByRef, allowing the plugin to return status data)
  2. Plugin returns -- flag set to 2 (encryption complete)
  3. RDEC arrives -- only executes if flag is exactly 2, then calls DEC(hwid), resets flag to 0

This means the operator must complete encryption before decryption is possible. The state machine enforces the ransom workflow: encrypt first, decrypt only after.

Persistence and AV evasion

CommandFunction
installInstall persistence (via plugin)
uninstallRemove persistence (via plugin)
updateUpdate RAT binary
WDKillerNewDisable Windows Defender
WDPLWindows Defender plugin (separate from WDKillerNew)
AntiiResetSurvive system reset
PSleepPrevent system sleep
askuacPrompt for UAC elevation
botBot control
adminAdmin privilege check
killKill process
JMar / JMarKLL / SysKLLProcess killing variants

Before persisting, the RAT queries Select * from AntivirusProduct via WMI (namespace \root\SecurityCenter2) to enumerate installed security products.

It also sets ShowSuperHidden to 0 under Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced. This hides protected OS files in Explorer -- the RAT's dropped files become invisible even with "Show hidden files" enabled.

WDKillerNew kills Windows Defender. AntiiReset survives system reset attempts.

USB spreading

CommandFunction
USB.exeUSB worm payload name
WinBIt32-*USB worm variant name pattern

The USB spreader creates .lnk shortcut files on removable drives using WScript.Shell COM automation. Each shortcut has its TargetPath set to cmd.exe with arguments like /c start [payload] & start explorer [folder] & exit -- the payload runs, then the original folder opens so the victim sees what they expected. Icon paths are read from HKEY_LOCAL_MACHINE\software\classes\folder\defaulticon\ so the shortcuts look like normal folders.

XWorm v3.1 from the same campaign uses an identical USB.exe spreading mechanism.

Ngrok tunneling

CommandFunction
ngrokNgrok tunnel connection
InstallNInstall ngrok binary

Ngrok provides reverse proxy tunneling for C2 redundancy. If the primary DuckDNS domain is blocked, the operator can route C2 traffic through ngrok.

Live chat

CommandFunction
LLCHATStart live chat session
XchatExtended chat
SNoteSend note to victim

Real-time text chat between the operator and the victim. The operator can impersonate IT support, demand ransom, or tell the victim to take specific actions mid-intrusion.

Other commands

CommandFunction
hrdp / hrdp+ / RD- / RD+Remote desktop (connect, extended, disconnect, reconnect)
PassR / PC#Password recovery
EmailEmail credential theft
GrabberDCDiscord token theft
CookieST / cokiBrowser cookie stealing
injRunInject and run payload in target process
PEPE file execution
Pvbnet / vbb / cbbVB.NET payload execution variants
scriptScript execution
regfucRegistry operations
openhideHidden browser instance via internetexplorer.application COM
NETINSNetwork install
FURLURL fetch
UPtoSUpload to server
MapsPLUMaps plugin
JustFunUnknown (likely nuisance function)
### / $$$ / ^^^& / |||Signal/control codes

Plugin loading architecture

Every plugin DLL arrives from the C2 server. The loading path is:

  1. AES decrypt the incoming TCP stream (AES-128-ECB, key = MD5 of the separator)
  2. UTF-8 decode the plaintext bytes
  3. Split by the separator (XSXSXSX) -- A[0] = command name, A[1] = plugin DLL bytes (base64)
  4. Base64 decode A[1] to raw bytes
  5. Assembly.Load() the decoded bytes, get modules, get types, CreateInstance("Class1")
  6. NewLateBinding.LateCall() the target method on Class1

IL disassembly confirms 46 plugin loader call sites in the command handler. 43 load the plugin directly from C2 command data (A[1]). Three -- webcam, microphone, and audio -- cache plugin bytes in static fields and reload from cache on subsequent calls. All three cached plugins call method CON on Class1.

Separator rotation

The separator is not static. Six stsfld write instructions in the command handler update it to values received from the C2 server, which means the AES key also changes mid-session.


Infrastructure

The Nov10/Nov19 builds use vijdklet.duckdns.org:7575, which resolves to 45.58.143.254 (Sharktech, Amsterdam NL). This is the same IP hosting two PureLogs variants:

RATDomainPort
VioletWorm v4.7 (Violet RAT, Nov10/Nov19)vijdklet.duckdns.org7575
VioletWorm v4.7 (Violet RAT, Nov24)vigroup2125.duckdns.org2125
PureLogs (Mvfsxog.dll)ydspwie.duckdns.org9045
PureLogs (Qdjlj.dll)nhvncpure*.duckdns.org6757-6759, 8075-8076

The Nov24 build uses a second domain (vigroup2125) on a different port (2125). The naming convention is consistent: vi- prefix for VioletWorm domains, like vijdklet.

Three RAT groupings ran on separate infrastructure. The Sharktech Amsterdam server was split from the ServerAstra Budapest cluster (91.219.238.167) that hosted four commodity RATs (DcRat, AsyncRAT, VenomRAT, Remcos). XWorm V3.1 ran on a third server (12.202.180.133, AT&T, Baltimore MD). The layout separates operations: VioletWorm and PureCoder tools shared one server, commodity RATs shared another, and XWorm remained isolated.

As of February 2026, port 7575 (VioletWorm) is still accepting connections on 45.58.143.254.


Build comparison

PropertyNov10 buildNov19 buildNov24 build
SHA256d656bcfe...aceb98e6e2c5921e...72ec1fedcbd4cd4c...f07806
Assembly size1,432,360 bytes1,425,920 bytes1,029,120 bytes
Shellcode size1,464,594 bytes1,722,111 bytes1,067,778 bytes
Loader encryptionAES-256-CBC + 2x XORRC4 (per-file key)AES-256-CBC + 2x XOR
Injection methodexplorer.exe APCIn-process CFUNCTYPEexplorer.exe APC
C2 domainvijdklet.duckdns.orgvijdklet.duckdns.orgvigroup2125.duckdns.org
C2 port757575752125
XOR dispatch keyynNENILynNENILXSRSXSX
MutexaXTyo1HpFXkKUYoLaXTyo1HpFXkKUYoLH3n0qlXPeIv1umQI

The Nov10 and Nov19 builds share the same C2 endpoint and XOR key -- the attacker recompiled between waves (assembly size changed by 6,440 bytes) but didn't change the configuration. The Nov24 build is a distinct compilation: different C2 endpoint (vigroup2125 instead of vijdklet), different XOR keys, different mutex, and 400 KB smaller. It was recovered from an unobfuscated developer artifact (1uunov24.py), one of only two unobfuscated loaders in the entire campaign.

Deployment history

WaveLoader filenameEncryptionInjectionC2
Wave 2 (Nov 10)BKSNOLazyNov10_Viooooooo.pyAES-256-CBC + 2x XORexplorer.exe APCvijdklet:7575
Wave 3 (Nov 10)BKNOLazyNov10_Viooooo.pyAES-256-CBC + 2x XORexplorer.exe APCvijdklet:7575
Wave 4 (Nov 19)1vio-obf.pyRC4 key NOpzga4kIn-process CFUNCTYPEvijdklet:7575
Nov 241uunov24.py (dev artifact)AES-256-CBC + 2x XORexplorer.exe APCvigroup2125:2125
Wave 5 (Nov 19)OBKSLazyNov20_vio.pyRC4 key NQzn2pMoIn-process CFUNCTYPEvijdklet:7575
Wave 5 (Nov 19)WBKSLazyNov20_vio.pyRC4 key Hs3TFDx1In-process CFUNCTYPEvijdklet:7575
Dec 21Dec2vio.pyKramer .pyc RC4In-process CFUNCTYPEvijdklet:7575
Wave 7 (Dec 16)via Shoopify.batSame payloadSamevijdklet:7575

VioletWorm was deployed in every wave from Wave 2 onward. Eight separate loaders with different encryption keys deliver the RAT across two C2 endpoints. The Nov24 1uunov24.py is an unobfuscated developer artifact -- one of only two in the campaign -- and is the only loader that delivers the vigroup2125 build.


Attribution

VioletWorm (also tracked as Violet RAT) is a separate MaaS product, not part of the PureCoder suite (PureLogs, PureCrypter, PureHVNC, PureMiner, PureRAT). The threat actor purchased tools from at least two MaaS vendors and deployed them together.

The C2 IP 45.58.143.254 is shared exclusively with PureLogs variants from the same campaign. No other RATs in the toolkit use this server. The attacker put VioletWorm and the PureCoder tools on one server (Sharktech Amsterdam) and the commodity RATs on another (ServerAstra Budapest). That's an operational choice by the buyer, not a developer-level connection between the tools.

VioletWorm is delivered through the same Python multi-stage loader infrastructure used by the other RATs in this campaign: same Donut shellcode version, same AES/XOR scheme, same injection methods, and the same Kramer obfuscator in later waves.

The attacker's internal label Viooooooo follows the same pattern as Ploggggggg (PureLogs), Asyncccc (AsyncRAT), Venommm (VenomRAT), and Annorii (DcRat) -- repeated last characters as padding. VioletWorm was always deployed alongside PureLogs. Both appear in the same loader sets, staging directories, and batch launchers.

VirusTotal classification

Neither sample had been submitted to VirusTotal before this analysis. On first submission, both scored 40-41/71. No vendor identifies it by name -- the dominant label is Gen:Variant.MSILHeracles, a generic Bitdefender heuristic for suspicious .NET binaries. ESET classifies both as MSIL/XWorm.L. IL comparison against an XWorm client confirms this is correct -- VioletWorm is a fork of XWorm with an expanded command set and different string encoding.

XWorm lineage

We compared VioletWorm's IL disassembly (27,623 lines) against an XWorm client sample (11,893 lines, SHA256 ced525930c76834184b4e194077c8c4e7342b3323544365b714943519a0f92af) across 10 structural dimensions. Five produced byte-identical IL sequences. Four were structurally similar. One differed.

Byte-identical IL:

  • AES implementation: Both use RijndaelManaged + MD5CryptoServiceProvider + ldc.i4.2 (CipherMode.ECB) with no IV. The encrypt, decrypt, and config-decryption methods match opcode-for-opcode.
  • HWID generation: Both build a 5-element object array (ProcessorCount, UserName, MachineName, OSVersion, DriveInfo.TotalSize), concatenate it, and pass it to an MD5 hash function. The IL from offset 0000 through 004e is identical in both binaries. Both use "Err HWID" as the fallback string.
  • Mutex creation: new Mutex(false, name, out bool). The close/dispose sequence (null-check, Close(), set null) also matches byte-for-byte.
  • C2 socket setup: new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp) with ReceiveBufferSize and SendBufferSize both set to 0xc800 (51,200 bytes), followed by BeginReceive for async I/O. Identical from the constructor through the buffer configuration.
  • Language: Both are VB.NET, not C#. Both reference Microsoft.VisualBasic as their first external assembly, use Operators.CompareString for string comparisons, and include identical My.MyApplication / My.MyComputer / My.MyProject scaffolding from the VB compiler.

Structurally similar:

  • Plugin loading: Both use Assembly.Load(byte[]) for in-memory plugin execution. XWorm searches for a type named "Plugin" with a method named "Run". VioletWorm searches by FullName suffix and uses CreateInstance().
  • Command dispatch: Both use a long if/else chain comparing A[0] against command constants via CompareString. XWorm stores commands as plaintext strings (~20 commands). VioletWorm XOR-encodes A[0] with key ynNENIL and compares against double-base64 constants (120 commands).
  • Class structure: 1:1 mapping between core classes -- settings/config, entry point, connection handler, command dispatcher, utilities (crypto + HWID + mutex), persistence. Same functional decomposition, different names.
  • Config class: Both use a single static class with a .cctor that initializes all fields. XWorm stores AES-encrypted base64 values (14 fields). VioletWorm stores 2-layer base64 values (30+ fields, with Google suffix appended to all names).

Different:

  • String encoding: XWorm encrypts config strings with AES-ECB using a key stored in the binary. VioletWorm replaced this with 2-layer base64 encoding (simpler, less secure). VioletWorm also added anti-decompiler marker classes (ObfuscatedByGoliath, NineRays.Obfuscator, NetGuard, dotNetProtector, YanoAttribute, Xenocode) that XWorm lacks.

The fork relationship is clear. The VioletWorm author took XWorm's codebase, replaced string encryption with base64 encoding, added XOR command encoding, expanded the command set from about 20 to 120, and added HTTP-based C2 address updates before socket connection. Core infrastructure remained the same: AES, HWID, mutex, sockets, and VB.NET late binding.

Vendor reporting

None of the existing campaign reports cover VioletWorm specifically. The closest are:

VendorReportCoverage
SecuronixAnalyzing SERPENTINE#CLOUDCampaign with overlapping tooling and TTPs (does not mention VioletWorm)
Check Point ResearchUnder the Pure CurtainPureCoder developer analysis (does not mention VioletWorm)
FortinetPureHVNC Deployed via Python Multi-stage LoaderLoader chain, PureHVNC component
eSentireQuartet of TroublePureLogs delivery via TryCloudflare tunnels

Indicators of compromise

Network

IndicatorTypeContext
vijdklet.duckdns.orgDomainC2 server (Nov10/Nov19)
vigroup2125.duckdns.orgDomainC2 server (Nov24)
45.58.143.254IPv4C2 IP (Sharktech, Amsterdam NL)
45.58.143.254:7575EndpointTria.ge behavioral endpoint (Nov10/Nov19 payloads)
213.227.152.82:2125EndpointTria.ge behavioral endpoint (Nov24 payload)
7575/tcpPortC2 port (Nov10/Nov19)
2125/tcpPortC2 port (Nov24)
XSXSXSXStringHTTP POST field separator
PING? / PONGStringKeepalive pattern

Host

IndicatorTypeContext
d656bcfefa98007fcb3e2be4430a9b24d258c046b0c768ac43699436aceb98e6SHA256Nov10 assembly
e2c5921e5c354000c38653d05ac3255865d20bc986da34e68246da6f72ec1fedSHA256Nov19 assembly
cbd4cd4c42b3ee0bfb99a254a97ff2c2aacc47d83e08601ae8cdf3b333f07806SHA256Nov24 assembly
<Violet>MutexRAT mutex tag
3d847c5c-4f5a-4918-9e07-a96cea49048dGUIDMutex / install filename
89c43fcf-5e52-4be7-a719-a26139ce636aGUIDSecondary mutex
VioletClient.exeFilenameOriginal assembly name (from PE headers)
ynNENILStringXOR dispatch key (Nov10/Nov19)
XSRSXSXStringXOR dispatch key (Nov24)
aXTyo1HpFXkKUYoLStringMutex (Nov10/Nov19)
H3n0qlXPeIv1umQIStringMutex (Nov24)
USB.exeFilenameUSB spreader
WinTempClean32.batFilenameCleanup script
\ngrok.exePathTunneling binary

Behavioral

  • HTTP POST traffic to port 7575 with XSXSXSX field separator
  • Tria.ge payload behavioral signature: Suspicious use of AdjustPrivilegeToken
  • Tria.ge loader behavioral signatures: Suspicious use of SetWindowsHookEx, Modifies registry class, Enumerates physical storage devices
  • PING? / PONG keepalive cycle
  • Rotating User-Agent strings across 6 platform profiles per session
  • Registry writes under HKEY_CURRENT_USER\SOFTWARE\ and HKEY_LOCAL_MACHINE\software\classes\
  • Hidden VNC session creation
  • .lnk file creation on removable drives (USB spreading)
  • ngrok process execution
  • Windows Defender tampering (WDKillerNew)
  • File encryption operations matching ransomware behavior
  • WMI query Select * from AntivirusProduct (namespace \root\SecurityCenter2)
  • ShowSuperHidden registry value set to 0 under Explorer\Advanced
  • taskkill.exe /pid for targeted process termination
  • JPEG screenshot capture with region bounds

Conclusion

Nothing about this binary is hard to reverse. The XOR key is in the binary, the encoding is stacked base64, and the C2 protocol is plaintext HTTP POST with no TLS. The difficulty isn't obfuscation -- it's that the interesting code isn't here. The 8,000-line command handler is a switchboard. Every capability worth analyzing lives in a plugin DLL that only exists on the C2 server, delivered at attack time and loaded once into memory. We can map every command the RAT accepts, but we can't see what those commands actually do once the plugin takes over.

The 11 encrypted blobs in the config class make this worse. They're initialized in the constructor, never referenced by the dispatcher, and don't decrypt with any key in the binary. They're there for the plugins to read -- and without the plugins, they stay opaque.

This post is the third in the series. PureLogs covered the plugin stager and crypto-stealing fat client. PureCrypter covered the loader infrastructure. Remcos Banking Fraud & AutoIt Persistence covers the AutoIt-based persistence chains that kept everything alive -- including the discovery that PureHVNC shares exact C2 infrastructure with PureLogs. VioletWorm is the payload the attacker put on the Sharktech Amsterdam server alongside the information stealer, and the one they deployed in every wave from November onward.

K

Kirk

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