In the first post we followed a fake CAPTCHA on a hacked Bangladeshi website through a Polygon smart contract to a password-protected archive. Two things were left hanging: there were two contract addresses in the injected page, and the archive contents were not the same for both.
This post is about what came out of those archives. They look like two unrelated malware families. They are not — underneath a very different set of wrappers, both are the same commodity infostealer. What actually differed between the two victims was one field in a builder.
The split
Eight loaders were injected into that one page. Six queried one contract, two queried the other. A single visitor ran all eight, so one page load could feed both chains.
Which contract answered decided which domain served the archive, which decided which executable was inside it:
- Chain A —
cloudeccentric[.]ccserved GoldHealth.7z, passwordOqnaUrMAXScXtKCF - Chain B —
id-verif-code[.]infoserved a .zip, passworditswar
Everything before that point was byte-for-byte the same machinery. Everything after it diverges immediately.

Chain A: nothing it runs is its own
GoldHealth.exe is a Microsoft IExpress self-extracting archive — a legitimate Windows packaging format, which is most of the reason it is unremarkable to anything watching.
IExpress stores its whole configuration as plain text in a resource, so you can read the answer without disassembling a single instruction. Two directives matter, and the order they appear in is a trap:
RUNPROGRAM→ nslookup.exe /? followed by word salad. Prints nslookup's help. Does nothing.POSTRUNPROGRAM→ "AutoIt3.exe" San.a3x. This is the real launch.
Both execute. An examiner who reads the first directive, sees a harmless diagnostic command, and stops there concludes the file is benign. This is the single most likely place to get this sample wrong, and it costs nothing to avoid: read both directives.
The cabinet inside holds exactly two files. One is AutoIt3.exe — the genuine, validly signed AutoIt v3.3.16.1 interpreter, from the vendor. The other is a compiled AutoIt script.
This is bring-your-own-interpreter, and it is the whole trick: neither file on disk is malicious in isolation. The interpreter is real and its signature checks out. Signature and reputation controls have nothing to fire on, because the chain never runs an unsigned binary of its own. The malice is entirely in the script it is pointed at.
Detection here cannot key on either file. It has to key on the relationship — a signed AutoIt3.exe running a .a3x out of a user-writable temp directory.
The script itself is machine-obfuscated to a degree worth describing, because most of it is designed to waste your afternoon. String encryption across 5,127 call sites. 320 While/Switch blocks that look like control-flow flattening but are not: each one resets its state variable at the top of every iteration, so exactly one case is reachable and it exits the loop immediately. The other cases are decoration. Once you notice that, the entire layer collapses mechanically. Constants are expanded into sums and BitXOR chains. Deobfuscated, 18,948 lines became about 1,000.
What those thousand lines do before running anything:
- Unhook ntdll. Maps a clean copy from \KnownDlls and overwrites every executable section of the loaded one, stripping EDR's inline hooks.
- Blind ETW. EtwEventWrite and its variants patched to mov eax,0 / ret.
- Blind AMSI. Same stub over AmsiScanBuffer.
- Detect sleep-skipping. Brackets its own Sleep calls with QueryPerformanceCounter and exits if the wall clock disagrees by more than 500 ms — which defeats the sandbox trick of fast-forwarding through delays.
- Detect DNS sinkholing. Resolves a hardcoded nonsense hostname with microsoft[.]com as a control. If the nonsense name resolves, something is answering everything, and it exits.
- Check the environment. Exits on hostnames tz, NfZtFbPfH, ELICZ or username test22.
- Adapt to AV rather than exit. Sees avastui.exe → sleeps 20 seconds. Sees bdagent.exe → sleeps 160. Sees avp.exe → changes its process creation flags.
Then it decrypts the payload it carries — RC4 with key 941408, implemented as injected shellcode rather than AutoIt code, then LZNT1 decompression — and hollows it into %SystemRoot%\System32\TapiUnattend.exe.
That target is a good choice by the attacker and a gift to defenders. TapiUnattend.exe is a legitimate signed Microsoft telephony component that is essentially never executed on a normal endpoint. When the loader is not elevated it also reparents the process to explorer.exe using PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, so your telemetry will show explorer.exe as the parent. That is a signal to alert on, not a reason to write the event off as user-initiated.
No persistence was found for the stealer. Infostealers frequently do not persist — they run once, exfiltrate, and rely on the stolen credentials. Absence of persistence is not evidence a host was clean.
Chain B: seven stages and a RAT
trust.exe is a MinGW-built binary that reads its own file, decodes a region past the end of the PE, and writes the result to %LOCALAPPDATA%\Realtek\RtlEx64.exe. Its import table tells you nothing, because the APIs it needs are resolved by hash at run time.
Two details matter operationally.
The directory name impersonates the Realtek audio driver vendor, which does not install executables under %LOCALAPPDATA% — genuine Realtek software lands in Program Files or the driver store. And the file is named …64.exe but is a 32-bit i386 PE.
Overlay Padding: hash-blocking the dropped file does not work. Before writing it, the dropper appends 16 to 47 random bytes generated from a counter seeded with GetTickCount() XOR GetCurrentProcessId(). The file hashes differently on every single execution. Only the first 0x9e000 bytes are constant. Detect on path and behavior.
The stealer it drops defeats Chrome's App-Bound Encryption — the protection Google shipped in Chrome 127 specifically to stop cookie theft. It does this the hard way: a dedicated x64 module is manually mapped into memory, calls CoCreateInstance against Chrome's own IElevator interface, lowers the proxy security with CoSetProxyBlanket, and gets the elevation service to unwrap the key for it. The cleartext key is written to C:\ProgramData\*.txt.
That module has no network APIs at all. The process that reads Local State is not the process that exfiltrates. If you scope an investigation to network-active processes, you will miss it.
Then it fetches a second program from hxxps://labassist[.]vectorground[.]net/AQ.exe. The URL is embedded in the payload at build time. There is no captured C2 response establishing a live instruction, and the process capture we hold covers the second stage's detonation, not the fetch that requested it. What is evidenced is ordinary build-time configuration.
StealC ships a second-stage loader as a standard feature — loader, create, run_as_admin, with EXE, MSI and PowerShell delivery paths. Chain B's build has the EXE path filled in with a URL. Chain A's build has the same capability with nothing in it.
What that URL delivers is AQ.exe, a .NET binary presenting itself as a "CPU Software Renderer 3D" demo, which carries its next stages encoded in the pixel values of bundled images — a small bitmap and a 12 MB PNG. Each is read back into memory and loaded reflectively, so those stages never exist on disk. There is no file for on-access scanning to see. The final one establishes persistence, adds a Defender exclusion, attempts a CMSTP UAC bypass, and hollows the last payload into a suspended child process.
That last payload is Overlord RAT: Go, obfuscated with garble, UPX-packed with the UPX! magic stripped so the standard unpacker refuses it. It talks WebSocket to port 5173.
It was still answering when we looked.

And it beacons persistently. Pointed at a listener in the lab, the implant retried every thirty seconds, indefinitely:

One thing to flag for anyone triaging this chain: the terminal binary's version resource claims OriginalFilename = stealc.exe. This is attacker-controlled build metadata deliberately set to mislead analysts, as the binary is actually written in Go, not StealC's typical C++.
They are the same stealer family of StealC
This is the finding that reorganizes the incident.
The evidence is mechanism-level, which is the right kind — not "both steal browser passwords", which every stealer does, but structural choices a builder makes that survive across builds:
- A non-standard
.fptablesection in both, holding pointers resolved at run time. Zero at rest. - RC4 protecting the string tables in both, with the key stored in plaintext inside the encrypted table — a construction that gives itself away because the key entry has a length that is not a multiple of four and therefore cannot be valid base64.
- APIs resolved at run time by parsing PE exports rather than importing them.
- The Chrome App-Bound Encryption modules in both have an identical section set, in identical order: .text .rdata .data .pdata .fptable .reloc.
- The same loader / create / run_as_admin second-stage grammar, with the same EXE / MSI / PowerShell triad.
Two differences that look significant and are not. Chain B's stealer is PE32 i386 where chain A's is PE32+ x64, and it carries CRT imports where chain A has none. Neither discriminates, because chain B's binary contains Heaven's Gate — the 0x33 far-call WoW64 transition — so it is a 32-bit wrapper executing 64-bit code and mapping an x86-64 module. Architecture here is a packaging choice, not a family boundary.
On confidence, plainly. The identification is StealC, and it is a working hypothesis that has not been verified against a primary vendor writeup. Internal consistency across the two samples is high. The one external anchor is a recurring build tag, newlife, which the engagement analyst reports appearing across unrelated StealC samples — suggesting a builder default rather than anything unique to this campaign. Anyone repeating the family name downstream should carry that caveat with it. Nothing about the recommended response changes either way.
Side by side
Everything above, in one place. Rows spanning both columns are what the two campaigns share; anything split is a real divergence.

The shape of that table is the point. The delivery layer is identical, the stealer is the same family, and the divergence is concentrated in two places: the wrappers each affiliate chose, and whether they populated the second-stage field.
What this means for response
Treat a StealC infection also as a potential loader event, not only as credential theft. The capability ships in every build, and this campaign shows one affiliate populating it and one not. Which one a given victim got is decided at build time, so determine it per sample rather than assuming — and assume credential compromise regardless.
For anything that ran either chain:
- Isolate the host. Both chains steal browser passwords and session cookies, wallets, and whatever else the C2 asks for.
- Invalidate active sessions, do not only reset passwords. These steal session cookies. A password reset alone leaves a hijacked session live, and chain B specifically defeats the Chrome protection designed to stop exactly that.
- Assume cloud-synced exposure. The configuration includes a from_sync collection target, so synchronized browser profiles are in scope and the blast radius may exceed the endpoint.
- Do not assume a clean host after removal. The loader capability means a second payload may already be resident, and in chain B's case it definitely was.
- The collection targets are sent by the server at run time. The file tells you what could be requested, not what was taken. Scope the response to the capability.
Detection
The highest-value host detections here are the ones that do not depend on a hash, because chain B's hash changes on every run and chain A's payload only exists in memory.
TapiUnattend.exe doing anything at all. It has no legitimate reason to execute or to talk to the network on a normal endpoint.
title: TapiUnattend.exe Making Network Connections
logsource:
category: network_connection
product: windows
detection:
selection:
Image|endswith: '\TapiUnattend.exe'
condition: selection
falsepositives:
- None expected. This binary should never originate network traffic.
level: criticalPair it with the process-creation equivalent. Legitimate execution would come from an unattended-setup or sysprep workflow parented to a provisioning process; anything parented to explorer.exe, a browser, an Office application or a script host is malicious.
A non-browser process reading a browser's Local State. This is the App-Bound Encryption theft, and it covers both chains because both handle ABE the same way.
title: Chrome App-Bound Encryption Key Theft via Elevation Service
logsource:
category: file_access
product: windows
detection:
selection_localstate:
FileName|endswith:
- '\Google\Chrome\User Data\Local State'
- '\Microsoft\Edge\User Data\Local State'
- '\BraveSoftware\Brave-Browser\User Data\Local State'
filter_browsers:
Image|endswith:
- '\chrome.exe'
- '\msedge.exe'
- '\brave.exe'
- '\elevation_service.exe'
- '\MicrosoftEdgeUpdate.exe'
- '\GoogleUpdate.exe'
condition: selection_localstate and not filter_browsers
level: highBaseline your backup, EDR and DLP agents into the filter before deploying — they legitimately read browser profile directories.
Executables under %LOCALAPPDATA%\Realtek\. Genuine Realtek software never puts them there.
title: Stealer Dropped and Executed from LOCALAPPDATA Realtek Directory
logsource:
category: process_creation
product: windows
detection:
selection_path:
Image|contains: '\AppData\Local\Realtek\'
selection_name:
Image|endswith: '\RtlEx64.exe'
condition: selection_path or selection_name
level: highAnd for file-based hunting, the YARA rule worth having is the one keyed on the ABE module, because it matches both the standalone module and any carrier holding it unencrypted:
rule chrome_abe_key_stealer_module
{
strings:
$abe = "\"app_bound_encrypted_key\":\"" ascii
$chrome = "\\Google\\Chrome\\User Data\\Local State" ascii wide
$edge = "\\Microsoft\\Edge\\User Data\\Local State" ascii wide
$brave = "\\BraveSoftware\\Brave-Browser\\User Data\\Local State" ascii wide
$co1 = "CoCreateInstance" ascii
$co2 = "CoSetProxyBlanket" ascii
$co3 = "CoInitializeEx" ascii
$crypt = "CryptStringToBinaryA" ascii
$guidfmt = "%08lX-%04hX-%04hX-%02hhX" ascii
condition:
uint16(0) == 0x5A4D and filesize < 2MB and (
($abe and 1 of ($chrome, $edge, $brave))
or ($abe and $co2 and $guidfmt)
or (all of ($co1, $co2, $co3) and $crypt and 1 of ($chrome, $edge, $brave))
)
}The YARA rules were tested against the samples in this workspace and matched. They have had no clean-corpus false-positive run. The Sigma rules convert to valid SPL but have not been fired against live telemetry. There is currently no detection content at all for Overlord RAT — the OverlordAgent- Run-value prefix and the WebSocket on 5173 are the obvious starting points, and that gap is open.
Indicators — payloads
Defanged. Hashes are never defanged, and nothing here is truncated.
Chain A
- GoldHealth.exe, IExpress SFX —
e7684d2a1d585af6ce69288c765fd833413ba77591d32c18d36fd2ff60bef4f1 - AutoIt3.exe, genuine signed interpreter —
92c6531a09180fae8b2aae7384b4cea9986762f0c271b35da09b4d0e733f9f45 - San.a3x, compiled AutoIt script —
b4c6f71966e533cc2bae9d379c282f68a0b832b1c3805b71ecfc63b53ecf6e13 - StealC payload, PE32+ x64 —
5373a588ee4cc853cb5ef308bf8a0c9e785bc07acee0cc5580e4f5bec9bd3850 - C2:
hxxp://160[.]20[.]109[.]90, URI /passive — AS402492 HostCram LLC, Turkey - Mutex
lxm7Q2UEQfnF7S2w63Z· builder pathC:\builder_v3\build\· section name.fptable - RC4 key for the embedded payload blob:
941408 - RC4 key for the stealer string table:
om4Gt2Z9zetwZ0qs4h, stored in plaintext at file offset 0x83c78 - Build tag
newlifeat offset 0x83c58 - Hollowed process:
%SystemRoot%\System32\TapiUnattend.exe, parent spoofed to explorer.exe - Staging files:
v20.txt, passwords.txt, screenshot.jpg, system_info.txt
Chain B
- trust.exe, PE32+ MinGW-w64 —
74a6fa03981a6b2d72a3806802d27c2524fdff0b8f15d735346a7f380db5fc69 - StealC, dropped as RtlEx64.exe —
3e80d58f4d1b66758b616bcffb111418df82763616f69e924bce7e78f7f385e2(this file's hash changes on every execution — see above) - Chrome ABE key-theft module —
61ab1d22949eac0582e989ae065ec4caee9ac99998276317edda96735cd311fb - AQ.exe, .NET pixel-steganography loader —
77ef1da611dd5d60a5db5c17f588af53d079c456a2c5eaf5357382ae792e0600 - Intermediate stage from a bitmap, memory-only —
5211eea4e91d9fbec6c16f88f934feae8b5c659acbe8007683987a4633725d02 - Intermediate stage from a PNG, SessionVault_Manager.dll, memory-only —
5d3a87fa8a18f0d5cbdfb02a9be316e36c6e3f35f84fe73f33f15cc928b6247e - Overlord RAT, UPX-packed —
25dc0f927c132640f7a2b31ead2e1e3c631006a29cc9d42d205f95834db93793 - Overlord RAT, unpacked with IAT rebuilt —
7744f2d03749945f0170a3338162230d66e2a38c07863b4e4855d2534eca1a7c - StealC C2:
hxxps://cloud-flare-authenticator[.]click/→89[.]34[.]90[.]45, AS207043 Dedik Services - Second stage:
hxxps://labassist[.]vectorground[.]net/AQ.exe - Overlord C2:
wss://download-windows-update[.]live:5173→151[.]243[.]113[.]94, AS207043 Dedik Services - Also seen on the StealC C2 host:
update-microsoft-data[.]services,windows-update-local[.]rest - Historic certificate on the Overlord host:
darkfadeson[.]lol, first seen 2023-02-01 - Drop path:
%LOCALAPPDATA%\Realtek\RtlEx64.exe· ABE key output:C:\ProgramData\*.txt - Persistence:
%APPDATA%\<6 random chars>.exewith a matching HKCU\…\Run value; RAT installs underHKCU\…\Run\OverlordAgent-<8 hex>— the prefix is the pivot, the suffix is random per install - Also:
Add-MpPreference -ExclusionPathfrom a non-admin GUI parent, andcmstp.exe /au <path>.infwith a CorpVPN INF - Dropper overlay key:
0a3b3d3d28185b2f190c2d321b1201592c0c280a0904035d251d081126295822, API hashing seeded at 0x3d3d3b0a — the first four key bytes, reversed - AQ.exe stage delimiter:
$$$133$5$9$$$
Next: Following the Wallets: How ErrTraffic Rotates Its Infrastructure.