Post

The ledger keeps talking: mapping five EtherHiding operators, two kit families, and 1,829 confirmed hacked websites

A second EtherHiding contract, a funding link, and a fully revalidated victim count: 1,829 confirmed hacked websites across two distinct kit families.

The ledger keeps talking: mapping five EtherHiding operators, two kit families, and 1,829 confirmed hacked websites

The views and opinions expressed in this post are my own and do not represent those of my employer. This is a personal blog where I share research and things I’m learning.

A note on the numbers before anything else

Every number in this post has been through a full, independent revalidation pass: every on-chain fact re-checked live against a second, fresh RPC call; all originally confirmed sites re-fetched and re-decoded fresh, three independent passes each; and every operator’s candidate-host pool rebuilt from scratch rather than reused. That pass substantially changed the headline count twice over: first from 901 to 969 as the four smaller candidate-pool rebuilds came in, then to 1,829 unique confirmed compromised sites once the largest operator’s candidate pool - built from 108 known C2 domains, 4,993 candidate hosts - finished and added 870 newly-confirmed sites nobody had found before (871 raw confirmations, one of which turned out to be a bare IP address duplicating its own AWS hostname already in the list - caught while assembling a single, reviewable combined list per family, not by the original per-pass checker). Some of the increase is genuine growth in what the pivot could see; some of the original count has also been remediated in the meantime

  • both are real and expected for an active campaign, not contradictory. The complete, timestamped, fully scripted evidence trail - every check, every output file - is in the companion repo linked at the end.

Put plainly: this is a live campaign, not a closed case. Compromised sites get patched and remediated, and new sites get compromised, continuously - on both sides of this equation, all the time. Every number in this post is accurate as of the analysis date above (2026-07-11), not a permanent count. If you check any of these figures again later, expect them to have moved - treat every count here as a floor, not a ceiling.

TL;DR

Following up an earlier EtherHiding case led me to a second, unrelated contract using the same “get current C2” function signature - which turned out to be a sibling instance of a kit I’d already partially mapped in a prior post, funded directly by the same wallet. Untangling that pulled in a completely separate EtherHiding campaign too, and cross-checking all five operators’ confirmed-victim lists against each other (rather than just trusting each investigation’s own total), then independently revalidating every one of those lists from scratch, landed on 1,829 unique confirmed compromised websites across two genuinely distinct kit families - and at least 18 sites, not just one, that two unrelated crews had each hacked independently. Scale like this lines up with what ASD flagged in its own large-scale exploitation campaign advisory targeting website content management systems - mass, opportunistic CMS compromise is the norm here, not the exception.

If this is your fleet, do these first:

  • Treat a browser-originated eth_call POST to a public Polygon RPC host as an anomaly worth alerting on outright - neither of these kit families has a legitimate reason to be on an ordinary user endpoint.
  • If you run WordPress/CMS at scale, grep your own page source for new Function( sitting next to atob( and a byte-XOR loop - that’s the injected-loader shape shared across both families, regardless of the per-site key.
  • Watch the two operator wallets below (and any wallet they fund) as a leading indicator - a new updateDomain/setter call or a fresh outbound funding transaction predicts the next C2 rotation before it’s live.

Full IOCs and detection rules are at the bottom.

Another day, another ClickFix campaign

Another day, another ClickFix lure: a compromised WordPress site, a fake “verify you’re human” overlay, a clipboard-hijacked command waiting for someone to paste it into Run. I’ve pulled apart enough of these that the shape barely registers anymore - what made me stop this time was the command-and-control mechanism. I’d recently written about EtherHiding on Polygon - a C2 address read live off a smart contract instead of being hardcoded or resolved via DNS - and this campaign, currently serving its lure from mamkor[.]pro, was doing exactly the same thing. So I stopped to check: was this the same threat actor I’d already mapped, or a different campaign I hadn’t seen yet?

Turned out to be a different campaign entirely - same technique, different operator, different infrastructure. Instead of noting the coincidence and moving on, I did what I always do when a pattern repeats: I stopped looking at the malware and started digging into the infrastructure instead.

That decision paid off twice over. While cross-checking this campaign’s own list of compromised sites, one of them - cedahr[.]com - decoded cleanly, same as the others, except the contract address it pointed at wasn’t this campaign’s. It was a contract I’d never seen. Same kind of injected loader. Different address. That’s exactly the shape of thing that turned my very first EtherHiding case into three separate operators a few days ago, so I stopped and pulled that thread too instead of writing it off as noise.

What came out the other end: a fourth confirmed instance of the “BW panel” kit family from that first post - this one funded directly, on-chain, by the same wallet behind Operator 1 in that post - and confirmation, once I tested it directly, that the mamkor campaign is a genuinely separate kit, not a fifth instance of that same family. This post is the combined map: every EtherHiding operator I’ve got receipts for so far, what’s genuinely shared between them and what isn’t, and the total confirmed damage once you dedupe the victim lists against each other instead of just adding them up. It’s about the contracts, the wallets, the C2 domains, and the compromised sites - not a teardown of what either campaign’s payload actually does once it lands; that’s a different post for a different day. If you run a CMS fleet, treat this as a reason to check your own logs against the IOCs at the bottom, not just an interesting read.

Defender quick reference

FieldDetails
Activity typeEtherHiding (blockchain-resolved C2) across two unrelated ClickFix/website-injection kit families
Primary artifactsTwo distinct Polygon eth_call selectors (0xb68d1809, 0x38bcdc1c); 5 operator wallets; 1,829 confirmed compromised websites
VerdictMalicious - active, ongoing, multi-operator
ConfidenceHigh for the kit-family split and the funding link (both independently re-verified on-chain twice); the 1,829 figure is fully revalidated by fresh, independent re-fetch of every site plus a from-scratch candidate-pool rebuild for every operator
Key logsProxy/DNS for eth_call/eth_getStorageAt POSTs to public blockchain RPC hosts; your own web server logs for the injected-loader shape
First defender actionsBlock both families’ current C2s (below); alert on eth_call from non-wallet processes; if you run WordPress/CMS, check your own pages for the injected-loader shape
Detection opportunitiesOn-chain wallet/contract monitoring as a leading indicator; YARA/Sigma in the companion repo
False-positive notesNone known for the on-chain indicators; developer/web3-tooling traffic to the same public RPC hosts is the one thing to tune out

The attack at a glance (both families, generalised)

  1. Initial access - a legitimate WordPress/CMS site gets a small, obfuscated <script> injected into its pages, via a vulnerable plugin/theme or stolen admin credentials (root cause not established per-site in either investigation).
  2. EtherHiding - the injected script makes a read-only eth_call against a Polygon smart contract to fetch the current C2 domain. Nothing in the page source names the C2 directly.
  3. Server-side gating - the resolved C2 serves a config that decides who sees the lure (OS, referrer, cookie) and mints per-victim tracking state.
  4. ClickFix - a fake CAPTCHA/verification overlay writes a command to the clipboard and talks the visitor into pasting it into Windows Run.
  5. Payload delivery - a next-stage payload lands and runs. What it does once it’s running is a story for another post; this one stays on the infrastructure that got it there.

The part worth a whole post by itself is step 2. Because the C2 lives on a public, append-only ledger, reading its full history - not just its current value - turns a single victim’s lure into a complete map of the operator’s infrastructure, funding, and (with some legwork) their other victims.

Two kit families, one technique

While mapping the new sibling contract, I noticed the mamkor[.]pro campaign used the exact same underlying mechanism as the “BW panel” kit family - C2 read from a Polygon contract - and the two campaigns’ victim-hunting pivots kept crossing paths (cedahr[.]com, above, is a direct example). That kind of overlap can mean “same kit, different customer,” or it can just mean “same general trick, independently reused” - the only way to tell the difference is to test it, not eyeball the similarity. So I called both contracts on-chain, live, with both operators’ function selectors. A selector is the 4-byte fingerprint at the start of a contract call that tells it which function to run - every distinct “give me the current C2” function has its own, and a contract either implements a given selector or it doesn’t:

1
2
3
4
5
# mamkor/merabs's contract (0x08207B087F61d7e95E441E15fd6d40BEfd6eD308),
# asked for the OTHER family's selector
curl -s -X POST -H 'Content-Type: application/json' \
  --data '{"jsonrpc":"2.0","method":"eth_call","params":[{"to":"0x08207B087F61d7e95E441E15fd6d40BEfd6eD308","data":"0xb68d1809"},"latest"],"id":1}' \
  https://polygon-public.nodies.app
1
{"jsonrpc":"2.0","id":1,"error":{"code":3,"message":"execution reverted","data":"0x"}}

That reverts. Cleanly, every time. eth_call is a free, read-only query against a smart contract - anyone can make one, no wallet or API key required - and this one comes back empty because mamkor’s contract simply does not implement that function. Its own documented selector (0x38bcdc1c) works fine on the same contract and returns mamkor[.]pro. Running the same test the other direction - the “BW panel” contracts against mamkor’s selector - also reverts. These are two genuinely separate on-chain artifacts, not one shared kit wearing two hats. What they actually share is the general EtherHiding technique - reading a C2 address from a Polygon contract via eth_call - and nothing more specific than that. I’m calling them Family A and Family B for the rest of this post. The significance: a shared technique tells you nothing about who’s behind either campaign, but a shared contract interface would have been real evidence of a shared kit or operator - which is exactly why it’s worth testing directly instead of trusting the surface similarity.

Family A - the “BW panel” kit (selector 0xb68d1809)

This is the kit family from my authorization-cdn-etherhiding-clickfix post, and it now has a fourth confirmed instance. “BW panel” isn’t a name I invented - it’s what the injected script itself calls its own control logic on this operator’s victim sites. I went back and pulled a fresh, live copy rather than just repeat the claim:

1
2
3
4
5
6
7
8
9
// Decoded live from a currently-compromised BW-sibling victim site
// (single-byte XOR, key 223 - same scheme, still active)
const LOCAL_STORAGE_KEY = 'site_repair_state';
const HANDLER_EXPORT = '__BW_MODE_RUN__';
const MODE_FILE_MAP = {
  browser: 'v1.js', font: 'v2.js', recaptcha: 'v3.js', bsod: 'v4.js',
  silent: 'v5.js', cloudflare: 'v6.js', cf_update: 'v7.js',
  mac_recaptcha: 'v8.js', mac_cloudflare: 'v9.js'
};

The full decoded script - contract address, selector, and all - is saved in the companion repo, not just quoted here. The evidence that all four operators below are running the same underlying kit, not just a similar-looking one, is on-chain: all four contracts answer the identical, non-standard selector 0xb68d1809, and three of the four - Operator 2’s five contracts, Operator 3’s contract, and two abandoned test contracts from Operator 1’s own wallet - share byte-for-byte identical compiled bytecode, confirmed in my prior post by pulling and hashing the deployed code directly rather than by comparing the injected JavaScript. BW-sibling’s own contract wasn’t part of that original bytecode comparison, but it answers the same selector and its wallet was funded directly by Operator 1’s wallet, independently confirmed on-chain below.

The new one first, since it’s what kicked this off. cedahr[.]com’s decoded script pointed at contract 0x926d64543148dB649C4F877fE7ba4c693e01E288. Pulling that wallet’s (0xb0425bf235a2275735c8c5d668aa0273c65970b9) full transaction history showed something the other three operators in that kit family hadn’t done: a hybrid rotation mechanism. Most of the time it deploys a brand-new contract per rotation with the domain baked into the constructor - 83 of those between March and May 2026. But it also, separately, calls a setter function on some of those same contracts afterward - a smart-contract method that lets the contract’s owner overwrite a stored value (here, the resolved C2 domain) without deploying a whole new contract - updating 21 more domains that way, including one contract that got its domain changed five separate times over a month.

That distinction mattered for the hunt, and I got it wrong on the first pass too. My compromised-site hunt agents were told to check decoded victim scripts against only the seed contract - reasonable for the other three operators, which each stick to one or a handful of contracts, but wrong here. Most of what got flagged as “a different sibling operator” in the first batch turned out to be this same operator’s own victims, just infected while a different one of its 87 contracts happened to be live:

1
2
Batch 1 (26 domains), first pass:  0 confirmed / 50 "sibling" / 50 unconfirmed
Batch 1 (26 domains), corrected:  39 confirmed / 11 genuinely-other / 50 unconfirmed

Corrected and re-run against the full 87-contract, 90-domain set this operator actually controls, the hunt landed on 111 confirmed compromised sites.

The funding link is the real find here. This operator’s wallet received exactly one incoming transfer, ever - 50 MATIC, transaction 0x3fb177082f44050aea0debe7ecce9df56ac092889af475358572dbc2d98ab97b - from 0xCaf2C54E400437da717cF215181B170F65187aBf, 14 minutes before the wallet’s first contract deployment (11:14:35 to 11:28:35 UTC on 2026-03-05). That’s Operator 1’s wallet from my prior post, the one I’ve been calling “xdav.” Reading a ledger cuts both ways: it doesn’t just expose one operator’s own infrastructure, it can expose who’s bankrolling a second one.

Revalidation update. I went back and independently rebuilt every one of the numbers above rather than trusting the first pass: re-fetched and re-decoded all four operators’ confirmed lists fresh (three independent passes each), and rebuilt the candidate-host pools from scratch instead of reusing the original search results. Two things moved, in opposite directions. Some of the original sites have since been cleaned up - only 106 of Operator 1’s original 123 and 56 of BW-sibling’s original 111 still showed the live injection on re-fetch. But BW-sibling’s candidate pool, rebuilt from scratch across its full 90 known domains, came back more than four times larger than the pool the original hunt had found (1,265 hosts versus roughly 285-393) - and pivoting that larger pool through the same decode logic turned up 167 further confirmed sites nobody had found the first time, plus 9 more that decode to Operator 1’s contract specifically. Net effect for Family A: 348 unique confirmed compromised sites, up from the 264 originally found, even after accounting for the sites that got remediated in between. Full per-operator breakdown, scripts, and output files are in the companion repo’s revalidation-2026-07-11-full/ folder.

The rebuild also turned up two things worth flagging rather than smoothing over. First, of BW-sibling’s 87 contracts, two carry placeholder values instead of real domains - one decodes to the literal string test - the same “abandoned test deployment” pattern I’d already documented for Operator 1’s wallet in my prior post, just not previously noticed here. Second, 14 sites show up as confirmed under both Operator 1’s kit and BW-sibling’s - independently, by two separate decode passes using two separate operators’ known-contract markers. I’m not resolving that one in this post; it could be a genuine double-compromise at a larger scale than the single site below, or a classification gap in one of the two checkers, and it deserves a manual look before anyone cites those 14 individually. These 14 are counted once, not twice, in the totals below.

One more honest loose end, unchanged by the revalidation: a domain, hahletsgoagain[.]beer, shows up in both the new operator’s 90-domain list and Operator 2’s history. I checked for a direct funding link between xdav and Operator 2’s wallet the same way I found the confirmed one above, and found nothing in the window I queried. So that’s a probable pivot for someone with more on-chain history to pull, not a claim I’m making here.

Here’s where all of that lands, final numbers, not the original-discovery ones above:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
                        FAMILY A -- "BW panel" kit (selector 0xb68d1809)
                        =================================================
                        (full contract/wallet addresses in the IOC table below)

   xdav wallet [Operator 1's wallet]
        |
        |-- owns/updates --> Operator 1 contract (25 updates) --> 113 compromised sites
        |                                                          (106 reconfirmed live + 9 more found
        |                                                           via BW-sibling's rebuilt candidate pool)
        `-- funds (on-chain, confirmed - tx hash in prose above) --> BW-sibling wallet
                                                    |
                                                    `-- 87 contracts / 90 domains --> 223 compromised sites
                                                        (56 reconfirmed live + 167 newly discovered)

   Operator 2 wallet -- 5 contracts, byte-identical template --------------> 24 compromised sites
        (domain-name overlap only with BW-sibling: hahletsgoagain[.]beer -- NOT a confirmed funding link)

   Operator 3 wallet -- 1 contract --------------------------------------> 2 compromised sites

              Family A total: 348 unique confirmed compromised sites
              (raw sum across all four operators above is 362; the 14 sites
              shared between Operator 1 and BW-sibling, noted above, are
              counted once here, not twice)

Family B - the mamkor/merabs kit (selector 0x38bcdc1c)

This is the campaign that started this post. At a high level: compromised WordPress site, EtherHiding-resolved fake-CAPTCHA ClickFix, a downloaded loader that decrypts and runs a next-stage payload in memory. I’m not going deeper than that here - what the payload actually does once it’s running is out of scope for this post, which is about the infrastructure that delivers it, not a teardown of the malware itself.

The infrastructure is the interesting part anyway. One persistent contract (0x08207B087F61d7e95E441E15fd6d40BEfd6eD308), 127 setter calls reconstructing what I originally logged as 102 historical C2 domains back to March 2026, and 638 confirmed compromised sites found by pivoting all of them through urlscan’s historical scan data and re-decoding each candidate’s own injected script (four different obfuscation schemes were in play across the population - cleartext, two XOR variants, and a 256-byte S-box substitution table).

Independently re-fetching and re-decoding all 638 sites fresh, three passes, reconfirmed 629 of them live. The 127 setter calls and the March-to-July date range both re-derived exactly on a second, independent pull; the 102-domain figure is closer to 108 once every one of the 127 raw domain strings is decoded fresh and normalised - a small refinement, not a contradiction.

That would have been the end of it, except this operator turned out to have the same story as BW-sibling above, just bigger. Rebuilding the candidate-host pool from scratch across all 108 known C2 domains - the same treatment BW-sibling got - returned 4,993 candidate hosts. One domain alone, a likely shared third-party widget subdomain rather than campaign-dedicated infrastructure, accounted for around 70% of that pool, so I didn’t treat the raw number as a victim count. Running the same decode/confirm check against every one of the 4,355 candidates not already in the original 638-site list found 871 more confirmed compromised sites - live, independently decoded, matching this operator’s contract and selector. One of those 871 turned out to be a duplicate hiding in plain sight: a bare IP address and that same IP’s AWS EC2 reverse-DNS hostname (ec2-100-31-95-216.compute-1.amazonaws.com) both got confirmed independently, because the checker deduped by exact string match and never noticed they’re the same host. Caught while building a combined, reviewable list of every confirmed site rather than trusting the per-pass count alone

  • exactly the kind of thing that only surfaces once you actually look at the full list instead of just the number. Combined with the 629 reconfirmed above, that’s 1,499 distinct confirmed compromised sites for this operator, not 1,500.

Family B is a single operator, not four, so there’s no funding hub to draw

  • but the scale is worth the same visual treatment Family A got above:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
                        FAMILY B -- mamkor/merabs kit (selector 0x38bcdc1c)
                        =============================================================
                        (full contract/wallet address in the IOC table below)

   mamkor/merabs wallet
        |
        `-- owns/updates --> persistent contract (127 setter calls, 108 known C2 domains)
                                    |
                                    |-- 629 of the original 638 compromised sites reconfirmed live
                                    |
                                    `-- 870 further compromised sites confirmed from a freshly
                                        rebuilt candidate pool (108 domains -> 4,993 candidates,
                                        871 raw confirmations minus 1 IP/hostname duplicate)

                                            Family B total: 1,499 unique confirmed compromised sites

The sites more than one crew hacked

Once I had all five operators’ confirmed-victim lists in hand, the obvious next question was whether any of them overlapped - not as a guess, as a direct set comparison. Diffing every pair of Family A operators against each other came back clean except one: the 14-site Operator-1/BW-sibling overlap already flagged above. Every other Family A pair - different wallets, different infrastructure - showed zero shared victims, which is what you’d expect.

Family A against Family B is a different story, and it kept growing as the revalidation went on. The first pass found exactly one match: www[.]beltboutique[.]co[.]uk, independently confirmed compromised by both Operator 1’s kit and mamkor/merabs. Rebuilding BW-sibling’s candidate pool found seven more. Once mamkor/merabs’s own candidate pool was fully rebuilt too - 108 domains, 4,993 candidates - the same diff found eighteen sites total: www[.]beltboutique[.]co[.]uk, agluona[.]lt, belindabuck[.]com, bigbaer[.]co, clearskyfarms[.]com, pathtohomeapproval[.]com, tractor-shop[.]ro, www[.]namathejaljawdah[.]com, aglimitless[.]com, careyestatesltd[.]co[.]uk, clearlinewebdesign[.]com, dawgonllc[.]com, engelspakistan[.]com, foodclub[.]ae, nickkyhub[.]com[.]ng, nickkyonline[.]store, noscalpelvasectomy[.]com, and rankandtrack[.]com - each one independently confirmed compromised by two completely unrelated operators running two completely unrelated kits. The count climbing from one to eight to eighteen isn’t the story getting less reliable each time - it’s what happens when the measurement finally catches up to the two operators’ actual, fuller candidate pools. Eighteen randomly selected small businesses each got hit twice by people who have no evident connection to each other, and given how much of mamkor’s own pool turned out to be previously unmapped, the true number is very likely still higher than that.

Combined numbers

Kit familyOperatorsConfirmed compromised sites (fully revalidated)
Family A (“BW panel”, selector 0xb68d1809)4 (xdav, Operator 2, Operator 3, BW-sibling)348
Family B (merabs/mamkor, selector 0x38bcdc1c)11,499
Cross-family duplicates removed--18
Grand total, unique51,829

That number comes from a script, not arithmetic in my head - normalise every list, union them, diff every pair. Spelled out: 348 + 1,499 = 1,847 sites if you just added the two family totals, but 18 of those sites are the ones from the section above, counted once in each family’s own total - subtract them back out once and you get 1,829, not 1,847. This table reflects a complete, independent revalidation: every one of the original 901 sites re-fetched and re-decoded fresh (three passes each), every on-chain fact re-checked against a second live RPC call, and every operator’s candidate-host pool rebuilt from scratch rather than reused. 1,829 is not a bigger version of the same 901 - it’s the net of real churn in both directions: some original sites have been cleaned up, and much larger freshly rebuilt candidate pools - especially mamkor/merabs’s, at 4,993 candidates against an original pool a fraction of that size - found genuinely new victims the first pass missed. Full scripts, every pass’s raw output, and a line-by-line log of what moved and why are in the companion repo’s revalidation-2026-07-11-full/ folder. Treat 1,829 as a floor, not a ceiling, same as every number in this series so far.

Techniques observed (MITRE ATT&CK)

The following techniques have been mapped to MITRE ATT&CK for future reference.

TacticTechniqueATT&CK IDWhat it did here
Resource DevelopmentCompromise InfrastructureT1584.004Hundreds of legitimate CMS sites injected as delivery infrastructure across both families
Initial AccessDrive-by CompromiseT1189Injected script served from each compromised site
Command and ControlWeb Service: Dead Drop ResolverT1102.001C2 resolved from a Polygon smart contract via eth_call - two distinct selectors, two distinct kit families
ExecutionUser Execution: Malicious Copy/PasteT1204.004ClickFix - victim pastes a command into Run
ExecutionCommand and Scripting Interpreter: PowerShellT1059.001Both families’ post-ClickFix stagers
Defense EvasionDeobfuscate/Decode Files or InformationT1140Polymorphic per-site injection decoding - the mechanism that makes hunting for compromised sites harder

Why this matters

The EtherHiding trick itself isn’t exotic anymore - I’ve already written about Family A in detail, and Family B works the same way underneath. What this combined view adds is scale and persistence: five distinct wallets, at least one of them directly bankrolling a second, running two unrelated pieces of infrastructure against a shared population of vulnerable CMS sites, for months, with a blockchain doing the one job that used to be the easiest thing for a defender to break - keeping the C2 address findable and blockable. Sinkhole a domain here and the operator loses nothing; one transaction and every compromised site on earth points somewhere new.

I’m not going to guess at who’s behind either kit family, and the “xdav funds a second operator” finding doesn’t change that - it tells me two operator wallets are financially linked, not who holds either wallet. What it does tell a defender, concretely, is that this is rented or shared infrastructure being used by more than one party at once, which is a durable reason to keep hunting for the technique (blockchain-resolved C2) rather than just today’s domain list. The gppcdnns[.]beer finding in the IOC section below points at the same thing one layer further back: even the two kit families, which are genuinely unrelated on-chain, appear to be drawing domains from a shared supply pipeline. Hunting the technique beats hunting any single domain list at every level of this stack.

What defenders can do

Technique (ATT&CK)What to doEssential EightWhat to hunt for
Web/blockchain C2 (T1102.001)Default-deny egress where practical; treat a browser-originated eth_call to a public Polygon RPC from a non-web3 user as an anomalyNo clean Essential Eight home - network architectureProxy logs for eth_call POSTs to polygon.drpc.org, polygon-public.nodies.app, polygon-bor-rpc.publicnode.com, 1rpc.io, rpc.ankr.com from ordinary users
Drive-by compromise (T1189)If you run WordPress/CMS at scale, audit your own page source for the injected-loader shape and rotate credentials/patch plugins on anything that matchesPatch Applicationsnew Function( next to atob( and a byte-XOR loop anywhere in your rendered HTML
ClickFix copy-paste (T1204.004)Application control catches the consequence even when the user types the cause; pair with user education that no legitimate site asks for a paste into RunApplication Control; User Application HardeningEvent ID 4688 where the parent is an interactive powershell.exe/cmd.exe with no arguments and the child touches a URL
Deobfuscation/decoding (T1140)Constrained Language Mode; Script Block Logging catches every XOR/reflection layer in cleartext as it evaluatesApplication Control; Securing PowerShell in the EnterpriseEvent ID 4104

Application control on user-writable paths is still the single lever that breaks the most stages at once across both families - the post-ClickFix stager and whatever it downloads next both execute from somewhere a user can write to (Implementing Application Control, November 2023). The one item that’s specific to this post rather than either individual campaign is the first row: watching the operator wallets themselves as a leading indicator. A new updateDomain/setter call, or a fresh outbound transfer from a wallet you already know is a kit operator, both happen on-chain before the new C2 domain is ever seen in the wild. If you’re already tracking one of these wallets for IOC purposes, that’s a free early warning that costs nothing but a periodic eth_call.

One pairing above is my own judgement call rather than a stock mapping: T1189 (Drive-by Compromise) doesn’t have its own row in the standard Essential Eight lookup, since the framework maps more naturally to the initial vulnerability than to the injection that follows it. I’m leaning on Patch Applications for it anyway - the injected-loader shape is a symptom, and the underlying cause on nearly every confirmed site here was a CMS running outdated plugins or themes, which is squarely what that strategy exists to fix.

Hunting and detection summary

  • Proxy/DNS logs for eth_call/eth_getStorageAt POSTs to public blockchain RPC hosts from non-browser, non-wallet processes - a general EtherHiding signal independent of either specific family.
  • Your own web server logs/page source for atob( + a byte-XOR loop feeding new Function( - the shared injected-loader shape, regardless of per-site key.
  • Event ID 4104 (Script Block Logging) for character-array/XOR-loop PowerShell bodies.
  • Event ID 4688 for interactive powershell.exe/cmd.exe with no arguments whose child touches a URL.
  • On-chain: periodic eth_call/wallet-history checks against the contract and wallet addresses below, watching for new setter calls or new outbound funding transactions.

The full YARA rule (Family B), aggregated IOC CSV, and the complete reproduction log behind every number in this post - including the two verification scripts that resolved the kit-family contradiction and the cross-family dedup check - are in the companion detection repo.

Indicators of Compromise

Blockchain contracts and wallets - Family A (“BW panel” kit, selector 0xb68d1809)

TypeValueNotes
Operator 1 / “xdav” contract0xB6bC9e1D0b2fB96Ab7C47E04Cb0BE477410bC1f2Live, 25 updateDomain calls as of revalidation (was 23 on 2026-07-08)
Operator 1 / “xdav” wallet0xCaf2C54E400437da717cF215181B170F65187aBfAlso directly funds the BW-sibling wallet below
Operator 2 contract0x83833C5D676cA06E941A32310AE67D0890F657eE5 contracts total, byte-identical deployment template
Operator 2 wallet0xf1940DDBDA56074ce29bB0b6eA8D62db974870a5Domain-overlap only with BW-sibling, no confirmed funding link
Operator 3 contract0x0C7Cb01C83203aC0a50Abc3a9AFF3c9Ca727eF551 contract, 4 updateDomain calls as of revalidation (was 3)
Operator 3 wallet0x2F9091AB4Ec91c0dAa67a7660c81A922328A80965 transactions total, ever, as of revalidation
BW-sibling seed contract0x926d64543148dB649C4F877fE7ba4c693e01E288One of 87 this operator has deployed/updated - 2 of the 87 carry placeholder/test values, not real domains
BW-sibling wallet0xb0425bf235a2275735c8c5d668aa0273c65970b9Funded directly by xdav, 50 MATIC, 14 minutes before first deployment

C2 domains - Family A

Operator 1 / “xdav” - 18 domains previously published, plus at least 1 more found during revalidation (25 total updateDomain calls now on-chain versus 23 originally documented - one further rotation’s domain value wasn’t captured in this pass): authorization-cdn-press-enter[.]info, authorization-code[.]info, authorization-id-code[.]info, authorization-code[.]beer, idverification-cdn[.]info, codeverificatrorcl[.]info, verificationscodes[.]beer, code[.]verification-claude-cdn[.]beer, claudverification-id[.]beer, idverification-code[.]beer, codecerification[.]beer, code-verification-js[.]beer, verification-code-js[.]beer, svs-verificationdate[.]beer, verification-js-cdn[.]boats, framework-css-styles-js[.]beer, ethercdnns[.]beer, xdavnode[.]pro, auth-code-check[.]info (current as of revalidation, rotated from authorization-cdn-press-enter[.]info).

Operator 2 - 3 domains (unchanged since original publication): iwannagetmoremoney[.]beer (current), hahletsgoagain[.]beer, letsgomakemoneyoncaptcha[.]beer.

Operator 3 - 4 domains previously published, plus 1 more found during revalidation (5 total, matching the on-chain count of 5 wallet transactions): hilacbatoriaaa[.]cc, pluhabovra[.]info, huishuvish[.]cc, errrkotmlkpoy[.]xyz, hihankidaha[.]cc (current as of revalidation).

BW-sibling - 90 domains (independently re-derived during revalidation; 2 of the 87 contracts carry placeholder/test values excluded from this list, current C2 is ns-claude-js[.]beer): anlytic-js-cloud[.]beer, awesomeisojs[.]beer, bcncdncl-ns[.]beer, best-claudns-js[.]beer, bilfojsclod[.]beer, biyaconserver[.]beer, bootstrap-maxcdn[.]beer, bootstrup-cdnmaper[.]beer, bootstrup-framework-js[.]beer, cash-js-server[.]sbs, cdn-2faclov[.]sbs, cdn-yethounds[.]beer, cdnjsdelivr[.]beer, cgfuryclaud[.]shop, chekbrow[.]beer, clacndjsvulnarbi[.]beer, clainasns[.]beer, claudesave[.]beer, claudjaframework[.]beer, cloud-save-image[.]sbs, cloude-js-server[.]beer, createbeer[.]beer, dhnsdns[.]beer, dncloteam[.]beer, fetestjs[.]beer, fijscdn[.]beer, fontawesome-js-cdn[.]beer, framesavecloudjs[.]beer, framework-jsoncdn[.]beer, frameworkjsbns[.]beer, fredcreate[.]beer, gdnssljs[.]beer, gppcdnns[.]beer (see cross-family overlap, below), graciasdenada[.]beer, hahletsgoagain[.]beer, hpscdn[.]beer, js-server[.]beer, jsframeworkns[.]beer, l3cdnns[.]beer, las-js-claud[.]beer, lcates-vs[.]beer, lckcdnjs[.]beer, ldnscreatejs[.]beer, lnfcdnclad[.]beer, localcloudcss[.]sbs, lsikjsns[.]beer, lskannsserv[.]beer, mistraljs[.]beer, neiwteamcdn[.]beer, nero-ns-cdns[.]sbs, npanssltejs[.]beer, ns-claude-js[.]beer, ns-cyber-server[.]sbs, ns-server-jscdn[.]beer, ns1cdnclaude[.]beer, nsbdnscloud[.]beer, nshtjscdn[.]beer, nslsconscloud[.]beer, nsserdns[.]beer, nsserv-bootstru[.]beer, nstdcs[.]beer, ntsnsdns[.]beer, nvbfcdnclaud[.]beer, olnsclaud[.]beer, sbnsdns[.]beer, sdhscndnssl[.]beer, shssshdscn[.]beer, siteamnsserv[.]beer, slndcdnclaud[.]beer, slngftr[.]beer, smetana-js[.]beer, smfcdnbb[.]beer, smtnscerver[.]beer, snccdn-framework[.]beer, sns-clauder-cdn[.]beer, ssg-cdn[.]beer, ssjscrybootstrup[.]beer, ssns-cdn-ns[.]beer, stabcdnvlc[.]beer, teamcss[.]beer, testerlau[.]lat, testesclaus[.]beer, unacerveza[.]beer, vdsinatest[.]beer, verification-cdn-cloud[.]beer, visual-ns-portal[.]beer, vjscloudjsns[.]beer, vnmstokns[.]beer, vsbnsbootstrup[.]beer, workcdnmass[.]beer.

Blockchain contract and wallet - Family B (merabs/mamkor kit, selector 0x38bcdc1c)

TypeValueNotes
Contract0x08207B087F61d7e95E441E15fd6d40BEfd6eD308Persistent, 127 updateDomain calls (exact match on independent re-derivation)
Operator wallet0x34c15320d6e8f59f1b66f6c191aaa7f87b894b66Sole setter
RPC endpoints abused (both families)polygon.drpc.org, polygon-bor-rpc.publicnode.com, polygon.lava.build, polygon.rpc.subquery.network, polygon-public.nodies.app, polygon-pokt.nodies.appLegitimate public infrastructure abused as the C2-resolution channel

C2 domains - Family B

mamkor/merabs - 108 domains (independently re-derived from all 127 setter calls during revalidation, bare-domain-normalized; current C2 is mamkor[.]pro): abrikos[.]xyz, abrmot[.]pro, allplanetssame[.]cfd, amalgama[.]lat, anakondabob[.]club, antongandon[.]club, ap7[.]supportly[.]au, arigatodomen[.]sbs, babybon[.]cfd, barmaleieba[.]lol, bearman[.]bond, bibliorock[.]lol, bigboysclub[.]cyou, biggestchlen[.]lol, biggestchlen[.]xyz, biletors[.]cfd, birdybird[.]rest, blatnoitovar[.]xyz, bobik[.]cfd, bulletpop[.]cyou, chinabowl[.]club, chinarice[.]asia, chubrik[.]sbs, cloudflare-check[.]cfd, comicstar[.]lat, corppop[.]shop, cosmostars[.]shop, denegnet[.]click, diddyparty[.]click, etomoe[.]cfd, etomoidomen[.]cfd, fesold[.]com, ganiballektor[.]cfd, gasshopper[.]sale, gdedengikarlos[.]cfd, gdelogi[.]lol, goodgoodmoon[.]bond, govnol[.]lat, gppcdnns[.]beer (see cross-family overlap, below), holopebamiy[.]bond, honeymoonshop[.]asia, ivangay[.]bond, kaloed[.]pro, krempie[.]xyz, krolikrojer[.]lat, lenders[.]digital, lizablud[.]shop, mamamiadomio[.]cfd, mambet[.]lol, mamkor[.]pro, mampodik[.]asia, marinaradom[.]cfd, marmelad[.]lat, mebanebols[.]trade, megamegalodon[.]click, mekasa[.]pro, memshowblob[.]forum, merabs[.]pro, merengagoi[.]bond, merindashop[.]cyou, merkantalolol[.]asia, mexicodreams[.]bond, microblob[.]bond, microblogver[.]bond, microchlen[.]lat, microloh[.]bond, milksos[.]cfd, misterslivker[.]asia, mistertwister[.]sale, mnepohui[.]sbs, mob[.]lanjut[.]in, moll[.]lanjut[.]in, myblobtop[.]site, mygoodblog[.]bond, mygoodblog[.]cfd, mylovedomen[.]asia, mymicroblog[.]lat, myverifyblog[.]sbs, nenadopapa[.]cfd, nihaoclub[.]asia, peachbro[.]bond, pilotkadomen[.]club, pinokros[.]xyz, pohuimne[.]lol, pokese[.]pro, ponikas[.]cyou, pringlesbob[.]cfd, productionmaza[.]bond, productionmaza[.]cfd, productionmaza[.]cyou, productionmaza[.]sbs, prokladka[.]lol, pusanik[.]shop, robodomain[.]sbs, sandman[.]bond, sandman[.]lat, sitepromclop[.]click, slivkishow[.]asia, smackit[.]lat, smenapodik[.]bond, spartanec[.]lat, superboomer[.]world, superpooper[.]click, superstarlog[.]click, thisismine[.]asia, whynotebanarot[.]xyz, yoshicity[.]xyz, zaserz[.]com.

Cross-family

Shared C2 domain, not just shared victims - the most surprising find in this pack. Building the full domain lists above for a proper cross-check (rather than just skimming for the “current” C2 per operator) turned up one domain both families have used: gppcdnns[.]beer. BW-sibling’s wallet deployed a contract with this domain baked into its constructor on 2026-05-28 09:46 UTC; mamkor/merabs’s wallet set the same domain via a setter call on 2026-05-28 22:26:45 UTC - roughly 13 hours later, same calendar day. Checked against both families’ full domain lists, this is the only overlap

  • not a pattern, one coincidence. I’m not reading this as evidence the two kit families are secretly connected (the on-chain interface test above already rules that out directly), but a same-day domain handoff this tight reads like both operators pulled from the same pool of freshly-registered, short-lived throwaway domains - a shared registrar, reseller, or bulletproof hosting pipeline sitting one layer further back than either kit itself. Worth a pivot for anyone positioned to look at domain registration data directly; not something I could take further from where I sit.
TypeValueNotes
Shared C2 domain (Family A and Family B)gppcdnns[.]beerBW-sibling (Family A) 2026-05-28 09:46 UTC; mamkor/merabs (Family B) 2026-05-28 22:26:45 UTC - ~13 hours apart, same day
Confirmed dual-compromise (18 sites)www[.]beltboutique[.]co[.]uk, agluona[.]lt, belindabuck[.]com, bigbaer[.]co, clearskyfarms[.]com, pathtohomeapproval[.]com, tractor-shop[.]ro, www[.]namathejaljawdah[.]com, aglimitless[.]com, careyestatesltd[.]co[.]uk, clearlinewebdesign[.]com, dawgonllc[.]com, engelspakistan[.]com, foodclub[.]ae, nickkyhub[.]com[.]ng, nickkyonline[.]store, noscalpelvasectomy[.]com, rankandtrack[.]comEach independently confirmed compromised by both a Family A operator and mamkor/merabs
Unresolved anomaly (14 sites)Full list in companion repoConfirmed under both Operator 1’s kit and BW-sibling’s kit by two separate decode passes - genuine double compromise or a checker classification gap, not yet reconciled

Sample hashes and payload-level detail for either campaign aren’t reproduced here - this pack is scoped to infrastructure (contracts, wallets, domains, compromised sites), not the malware. Full aggregated IOC CSV (all five operators, cross-referenced against my prior post’s full domain/site lists rather than duplicating them): companion repo, linked above.

Detection rules

Family A’s injected-loader-shape YARA already lives in my prior post’s companion repo - not duplicated here. Family B’s equivalent - it flags the injected EtherHiding loader script itself, not the payload it eventually drops (that’s out of scope for this post; payload-level detection rules, if you want them, are in the companion repo):

rule EtherHiding_ClickFix_Polygon_Loader_JS_FamilyB
{
    meta:
        author = "blueteam.cool (@btcoolteam)"
        description = "Injected EtherHiding ClickFix loader resolving C2 from a Polygon contract (Family B / mamkor-merabs kit, selector 38bcdc1c)"
        date = "2026-07-11"
    strings:
        $eth   = "eth_call" ascii
        $sel   = "38bcdc1c" ascii
        $rpc1  = "polygon.drpc.org" ascii
        $rpc2  = "polygon-bor-rpc.publicnode.com" ascii
        $cfg   = "a=tds_cfg" ascii
        $land  = "_landing" ascii
        $cook  = "_cf_verified=v" ascii
        $clip  = "clipboard-write" ascii
        $fn    = "new Function(" ascii
    condition:
        filesize < 50KB and $eth and $sel and $cfg and 3 of ($rpc1,$rpc2,$land,$cook,$clip,$fn)
}

Closing

The part of this that stuck with me wasn’t any single number - it was how much of this map only exists because I went back and tested claims I could have just as easily taken on faith. “These look like the same kit” felt true right up until I actually called both contracts and got a revert. “901 confirmed sites” felt like the number until re-fetching every single one of them, three times each, and rebuilding every operator’s candidate pool from scratch, moved it to 1,829 and turned one dual-compromised site into eighteen. None of that changes the shape of the story - it makes it sturdier. That’s the whole argument for treating a blockchain ledger the way you’d treat any other piece of evidence: read it yourself, run the numbers again, and don’t assume the first pass is the final word.

Stay curious.


On methodology: the investigation is mine. The reverse engineering and analysis assembly were carried out with AI workflows (Claude, primarily). I reviewed every finding. Errors are mine - ping me on X or Instagram if you spot something off.

References

This post is licensed under CC BY 4.0 by the author.