Note: The bulk of this analysis and write-up was produced with the Kimi K3 large language model.

Summary#

The sample analysed here is a four-stage .NET delivery chain that deploys AsyncRAT version 0.5.8. The outer binary poses as an Armenian-language water-cycle simulation, complete with a functioning particle engine, control panel, and live charts. Its two bitmap resources are not artwork. They are payload carriers, and the code that reads them uses Bitmap.GetPixel as a steganographic carving tool.

The chain is notable for three reasons. First, it uses two different pixel-encoding schemes across the two carrier bitmaps, each with anti-carving countermeasures. Second, its third stage is an Eazfuscator-wrapped loader built around a complete academic epidemiology simulator, which performs persistence, Microsoft Defender tampering, and process hollowing into signed .NET framework utilities. Third, the final payload is an essentially unmodified AsyncRAT client whose operator deliberately disabled its built-in installation and anti-analysis features, because the loader already provides them.

Every stage was extracted and decrypted statically, without executing the sample. The analysis also produced pixelchain, a keyless end-to-end extractor that recovers all four stages and the final RAT configuration from the dropper alone. Let us walk through the analysis.

Sample Information#

FieldValue
Filename938a9136aa1f128c6462899bac6ab7a6a7cfd3a1bcc79fb38bfb207032d8515b.exe (assembly title “WaterCycle”; referred to below as WaterCycle.exe)
File TypePE32 executable (.NET assembly)
Architecturex86 (IL-only)
Framework/Platform.NET Framework 4.5, WinForms
File Size778,240 bytes
SHA256938a9136aa1f128c6462899bac6ab7a6a7cfd3a1bcc79fb38bfb207032d8515b
MD581961b2997cdf859ab8b96c3dd0afb07
Entry Point0x000BF5DE
Compilation TimestampNot recovered (assembly claims copyright 2026)
Campaign/FamilyAsyncRAT delivery chain

Attack Chain Overview#

The stages unfold as follows:

  1. Stage 1: WaterCycle.exe: ConfuserEx-obfuscated decoy application. During form initialisation it carves stage 2 out of the RGB channels of the bitmap resource Fec and reflectively loads it.
  2. Stage 2: ToolBelt.dll: A second decoy (a puzzle game) hiding the second loader. It crops the Ozci bitmap, reads four bytes per pixel, XOR-decrypts, and reflectively loads stage 3.
  3. Stage 3: WinTune.exe: An Eazfuscator-obfuscated loader wrapped around a legitimate HIV-transmission simulator. It persists, tampers with Defender, and decrypts the embedded 4T86dNv resource into stage 4, executing it in memory or via process hollowing.
  4. Stage 4: AsyncRAT 0.5.8: The final RAT client. TLS with a pinned self-signed certificate to a dynamic-DNS C2, with all intrusive capability delivered as post-compromise plugins.

Stage 1: The Water-Cycle Decoy and the Fec Bitmap#

The dropper opens three windows: a particle simulation titled Simulyatsia, a control panel with Armenian labels, and a pie chart. The decoy is complete enough to survive both a casual user and a screenshot-driven sandbox verdict. The malicious work happens in the main form’s initialiser, Bm.Bt.bu(), wedged between button-setup code:

1
2
3
4
5
Assembly assembly = Assembly.Load(jZh.lZG(Resources.Fec));
Type type = assembly.GetExportedTypes()[0];
string[] array = YC.Split(new string[1] { "%3333f%" }, StringSplitOptions.None);
string[] array2 = new string[3] { array[1], array[2], array[0] };
object obj = Activator.CreateInstance(type, array2);

jZh.lZG walks the 142x142 bitmap Fec pixel by pixel, x-outer, y-inner, and appends the R, G, and B channel of each pixel to a byte stream, keeping the first 59,392 bytes. Notably, the pixel accessors are not called directly: the code builds them by compiling LINQ expression trees into Func<Bitmap,int,int,byte> delegates at runtime, a small JIT-level flourish that keeps static analysis tools honest. The delimiter string decodes to the constructor arguments 4F7A6369, 6A727A, and WaterCycle.

Stage 1 extraction flow: Fec bitmap to Assembly.Load

The carved bytes are a valid PE: stage 2, a 59,392-byte .NET DLL (SHA256 dd4ddcb93d369da8437b8f8f1386fec6a5b18251c94e0911c38540bb9a9fa280).

Stage 2: Crop, Read, XOR#

Stage 2 is ConfuserEx-obfuscated with Georgian-script identifiers and fronted by a puzzle-game form. Its constructor hex-decodes the stage-1 arguments into a resource name (Ozci) and key material (jrz), then runs the real loader:

  1. Retrieves the 584x632 Ozci bitmap from the host process resources.
  2. Crops it to 407x407, discarding 177 columns and 225 rows of noise padding.
  3. Reads four bytes per pixel (the little-endian bytes of Color.ToArgb(): B, G, R, A), taking a 4-byte little-endian length prefix: 659,969 bytes of ciphertext.
  4. Decrypts with Mist:
1
2
num3 = data[data.Length - 1] ^ 0x70;
out[i] = data[i] ^ num3 ^ keyBytes[i % key.Length];   // key = "jrz"

The key folds into a constant, so the effective keystream is the repeating three-byte sequence 82 E8 82. Two anti-carving details are worth calling out: the crop defeats naive full-image extraction, and the four-channel layout defeats RGB-only carvers. An RGB extraction of the same bitmap produces garbage, which is exactly the kind of trap that wastes an analyst’s afternoon.

Stage 2 decryption flow: Ozci bitmap, crop, BGRA read, XOR

The plaintext is stage 3, a 659,969-byte .NET executable (SHA256 f444c732baf179d7b6fb560fc3ec4ae5758696eb5cce06c6fb5f78f82cac6408).

Stage 3: The Eazfuscator Workhorse#

Stage 3, internally named WinTune, embeds an entire legitimate application: an epidemiological simulator titled “Simulation Models for the Control of the Dynamics of HIV Infection Through Vaccination”. The malicious logic hides under three Eazfuscator layers: encrypted strings, delegate-based call indirection, and invisible Unicode identifiers.

Recovering 638 encrypted strings#

Encrypted strings live in the resource HiddenEvent.CommonEvent and are resolved by five global generic methods of the form <Module>.?????<string>(uint key). Because the decryptors are pure functions, all 638 referenced strings were recovered by invoking them through reflection in a 32-bit PowerShell process. The haul reads like a capability manifest: registry Run-key persistence, Add-MpPreference -ExclusionPath, DisableAntiSpyware, the full process-hollowing API set (ZwUnmapViewOfSection, VirtualAllocEx, WriteProcessMemory, thread-context APIs), the hollowing targets vbc.exe, MSBuild.exe, and RegSvcs.exe, a CMSTP UAC-bypass INF, and its cleanup command taskkill /IM cmstp.exe /F.

The loader’s behaviour is governed by a pipe-delimited configuration string encoding a 17,743 ms startup delay, persistence and Defender-tamper flags, and injection mode auto. Critically for the “what does it drop” question: the string table contains no URLs and no download logic. The loader fetches nothing. Everything it needs is already inside it.

Persistence and Defender tampering#

The loader copies itself to %AppData%\ZGXPuDHEmsWfAQ.exe and registers a Run key. When elevated, it adds a Defender path exclusion through hidden PowerShell and sets HKLM\SOFTWARE\Policies\Microsoft\Windows Defender\DisableAntiSpyware = 1.

Stage 3 loader flow: delay, persistence, Defender tampering, payload execution

The 4T86dNv cipher#

The final payload occupies the resource 4T86dNv as a 46,081-byte encrypted blob; decryption discards the final byte, yielding 46,080 bytes of plaintext. The cipher is a chained XOR-subtract construction keyed on the string uJrxHaj:

1
out[i] = ((ct[i] XOR key[i mod klen]) - ct[(i+1) mod n] + 256) mod 256

Each output byte depends on the next ciphertext byte, which defeats single-byte known-plaintext recovery and keeps entropy high even over zero-filled PE regions. A subtle implementation detail, the loop runs n+1 iterations and double-processes byte 0 against the already-decrypted byte 1, must be replicated exactly, or decryption fails silently. This detail later became the difference between a working extractor and a broken one.

Execution: reflective load or process hollowing#

With stage 4 in hand, the loader picks one of two paths. The primary path is Assembly.Load plus EntryPoint.Invoke. The alternative is classic process hollowing into a signed Microsoft .NET utility (vbc.exe, MSBuild.exe, or RegSvcs.exe): spawn suspended, unmap, allocate, write, set context, resume. The hollowed compiler process then originates the C2 traffic, which looks considerably less suspicious than a random unsigned binary making TLS connections.

Stage 4: AsyncRAT 0.5.8#

The final payload (SHA256 4518eb287a5d1f7b5001b103d7e905e24d87148eaecb7972546d53f206e0e175) is a stock AsyncRAT client. Its configuration strings are AES-256-CBC encrypted, keyed by PBKDF2 (SHA-1, 50,000 iterations, fixed 32-byte salt) over a double-base64-encoded master key, each blob authenticated with HMAC-SHA256. An RSA signature over the key hash, verified against the embedded server certificate, binds the binary to its C2 certificate.

Decrypted configuration:

SettingValue
Hostsratsales.duckdns[.]org
Ports6606, 7707, 8808
Version0.5.8
Install / Anti / BDOSfalse / false / false
Pastebinnull
GroupDefault
MutexV3cyXIlQHKxp
Startup delay3 s

The client picks a random host/port pair, negotiates TLS pinned to the embedded self-signed certificate (CN=AsyncRAT Server, thumbprint 62CD77BEFFE28BE7AD8BFFE401163D99B7BB5DE2, issued 2026-07-12), and speaks MessagePack. The stub implements only three commands: pong, plugin, and savePlugin. Everything else, including remote desktop, keylogging, and credential theft, arrives as plugins cached DEFLATE-compressed under HKCU\Software\[HWID].

AsyncRAT C2 protocol sequence

Building pixelchain: A Keyless Chain Extractor#

Existing tooling stops at individual layers. AsyncRAT config parsers (rat_king_parser and friends) expect you to hand them the RAT. Eazfuscator deobfuscators (de4dot, EazFixer) expect you to hand them the loader. Nothing walked the chain. So the analysis produced pixelchain, which takes only the dropper and emits all four stages plus the decrypted configuration.

The design principle is no hardcoded secrets:

  • The stage-2 XOR keystream is solved from DOS-header known-plaintext (MZ plus the This program stub text), not recovered from the key string.
  • The stage-3 cipher key is recovered algebraically from the same known-plaintext, accounting for the double-processed first byte.
  • Bitmap geometry is brute-forced, and the discriminator between the ~120 candidates that share a valid-looking header is the BSJB CLR metadata signature deep in the file.
  • dnfile handles .NET metadata; self-contained minimal PNG/BMP decoders handle the carrier images (dnfile will not deserialise BinaryFormatter-embedded bitmaps).

Verified against the reference sample, it reproduces every stage hash byte-for-byte:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
[*] 2 images decoded from stage 1 resources
[+] stage2 59392 bytes  dd4ddcb9...9fa280
[+] stage3 659969 bytes  f444c732...cac6408
[+] stage4 46080 bytes   4518eb28...206e175
{
  "Ports": "6606,7707,8808",
  "Hosts": "ratsales.duckdns[.]org",
  "Version": "0.5.8",
  "MTX": "V3cyXIlQHKxp",
  "Group": "Default",
  "Install": "false", "Anti": "false", "BDOS": "false",
  "Pastebin": "null",
  "CertSHA1": "62CD77BEFFE28BE7AD8BFFE401163D99B7BB5DE2"
}

Network indicator defanged for publication; pixelchain prints the live value.

pixelchain ships with this post: pixelchain. Its only dependencies are dnfile, pefile, and pycryptodome.

MITRE ATT&CK Mapping#

TacticTechniqueIDObservation
Defense EvasionSteganographyT1027.003Payloads encoded in bitmap pixel channels (Fec, Ozci)
Defense EvasionObfuscated Files or InformationT1027ConfuserEx, Eazfuscator, AES-encrypted configuration
ExecutionReflective Code LoadingT1620Assembly.Load at every stage transition
Defense EvasionProcess HollowingT1055.012vbc.exe / MSBuild.exe / RegSvcs.exe via ZwUnmapViewOfSection
PersistenceRegistry Run KeysT1547.001Run-key entry for %AppData%\ZGXPuDHEmsWfAQ.exe
Defense EvasionDisable or Modify ToolsT1562.001Add-MpPreference exclusion; DisableAntiSpyware
Privilege EscalationAbuse Elevation Control MechanismT1548.002INF artefact used to bypass UAC (CMMGR32.EXE App Paths abuse)
Defense EvasionSystem Binary Proxy Execution: CMSTPT1218.003Malicious INF executed through c:\windows\system32\cmstp.exe, cleaned up with taskkill
Defense EvasionMasqueradingT1036Decoy GUIs; compiler processes as injection hosts
Command and ControlAsymmetric CryptographyT1573.002TLS with pinned self-signed certificate
Command and ControlNon-Standard PortT1571TCP 6606, 7707, 8808
Command and ControlDynamic DNST1568.001duckdns[.]org C2 hostname
Defense EvasionTime-Based Sandbox EvasionT1497.00317.7 s and 3 s startup delays

Threat Intelligence and Attribution#

AsyncRAT is an open-source RAT available on GitHub since 2019, operated by commodity actors through crypter services. Three external checks anchor this attribution. The stage-1 hash is indexed on ThreatFox and MalwareBazaar, tagged AsyncRAT, first seen 2026-07-17. NICTER’s published AsyncRAT analysis documents the identical hardcoded Aes256.Salt byte array recovered from stage 4. Malpedia’s family entry matches the observed capability set.

Decoy provenance#

The stage-3 decoy is a complete academic epidemiology simulator. Automated analysis platforms (unpac.me, FileScan.IO) host sibling samples built from the same crypter chain with the identical decoy, which indicates mass-produced packing output rather than a bespoke build. The certificate was minted 2026-07-12, five days before the sample surfaced.

Attribution confidence#

High confidence on family (AsyncRAT 0.5.8, NYAN-x-CAT lineage). No actor attribution: the infrastructure (dynamic DNS, per-build certificate, unreported mutex V3cyXIlQHKxp) is consistent with a commodity operator, and a MalwareBazaar community tag “Grim” attached to the sample is uncorroborated in public reporting. The Armenian-language stage-1 decoy is suggestive of regional targeting, but this remains speculation without further evidence.

Detection Engineering and Recommendations#

  1. Block and sinkhole ratsales.duckdns[.]org; alert on TLS to TCP 6606/7707/8808 and hunt for the pinned certificate thumbprint 62CD77BEFFE28BE7AD8BFFE401163D99B7BB5DE2. The operator’s secrecy mechanism is also their loudest signal.
  2. Alert on managed processes that enumerate an entire bitmap resource via Bitmap.GetPixel and immediately call Assembly.Load, which is the stage-transition signature.
  3. Alert on vbc.exe, MSBuild.exe, or RegSvcs.exe with no build context, suspended-child spawn patterns, or outbound TLS.
  4. Audit Add-MpPreference -ExclusionPath in PowerShell script-block logs (event 4104) and writes to the DisableAntiSpyware policy value.
  5. Sweep for the mutex V3cyXIlQHKxp, %AppData%\ZGXPuDHEmsWfAQ.exe, and the four stage hashes; deploy the YARA rule in Appendix A.

Conclusion#

Nothing in this chain is individually novel. The value is in the layering: two polished decoys, two distinct pixel encodings, an Eazfuscator loader that absorbs persistence and evasion duties, and a stock RAT kept deliberately thin. The operator disabled the client’s own install and anti-analysis features because the crypter already does that work, which is the signature of a packing service rather than a hands-on actor. Defenders should aim at the transitions (the pixel carving, the string decryptors, the hollowed compiler processes, and that very huntable pinned certificate) rather than the well-signatured final payload.

Indicators of Compromise#

File Hashes (SHA256)#

1
2
3
4
938a9136aa1f128c6462899bac6ab7a6a7cfd3a1bcc79fb38bfb207032d8515b  Stage 1 - WaterCycle.exe (dropper)
dd4ddcb93d369da8437b8f8f1386fec6a5b18251c94e0911c38540bb9a9fa280  Stage 2 - ToolBelt.dll (loader)
f444c732baf179d7b6fb560fc3ec4ae5758696eb5cce06c6fb5f78f82cac6408  Stage 3 - WinTune.exe (loader)
4518eb287a5d1f7b5001b103d7e905e24d87148eaecb7972546d53f206e0e175  Stage 4 - AsyncRAT 0.5.8 client

Host-Based Indicators#

1
2
3
4
5
6
%AppData%\ZGXPuDHEmsWfAQ.exe                                    Stage-3 dropped copy
HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Run              Persistence entry
HKLM\SOFTWARE\Policies\Microsoft\Windows Defender\DisableAntiSpyware = 1
HKCU\Software\[HWID]                                            AsyncRAT plugin cache
Mutex: V3cyXIlQHKxp
Processes: vbc.exe / MSBuild.exe / RegSvcs.exe (hollowing hosts)

Network Indicators#

1
2
3
ratsales.duckdns[.]org                                          C2 (dynamic DNS)
TCP 6606, 7707, 8808                                            C2 ports
TLS cert: CN=AsyncRAT Server, SHA-1 62CD77BEFFE28BE7AD8BFFE401163D99B7BB5DE2

References#

Appendix A: Detection Rules#

YARA Rule#

 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
rule AsyncRAT_WaterCycle_Dropper_Chain
{
    meta:
        description = "Detects the multi-stage bitmap-steganography dropper chain delivering AsyncRAT 0.5.8"
        author = "Rhys Downing"
        date = "2026-07-18"

    strings:
        $s1_jerma = "Jermastichan" ascii
        $s1_sim   = "Simulyatsia" ascii
        $s1_fec   = "Fec" ascii wide
        $s1_ozci  = "Ozci" ascii wide
        $s1_delim = "%3333f%" ascii
        $s3_file  = "ZGXPuDHEmsWfAQ" ascii wide
        $s3_key   = "uJrxHaj" ascii wide
        $s3_res   = "4T86dNv" ascii wide
        $rat_key  = "ID69TANHYImZXsqILbCTdHwQP1sqKtDd" ascii
        $rat_mtx  = "V3cyXIlQHKxp" ascii wide
        $rat_cert = "AsyncRAT Server" ascii wide
        $rat_ns1  = "Client.Handle_Packet" ascii
        $rat_ns2  = "MessagePackLib" ascii

    condition:
        uint16(0) == 0x5A4D and
        (
            // stage 1: require a strong anchor so the short "Fec"/"Ozci"
            // bitmap names cannot trigger the branch on their own
            (2 of ($s1_*) and 1 of ($s1_jerma, $s1_sim, $s1_delim)) or
            (2 of ($s3_*)) or
            ($rat_key) or
            (2 of ($rat_mtx, $rat_cert, $rat_ns1, $rat_ns2))
        )
}

This post was brought to you by Kimi K3 — the bulk of this analysis and write-up was produced with the Kimi K3 large language model.