Back to all posts
Published on · by Renaud Deraison

The command began with npm install

On August 24, 2026, Cato Networks' CTRL team published a campaign that buys sponsored Google results for "codex macos download", lands developers on a Google Sites page dressed as OpenAI's Codex portal, and asks them to paste a Terminal command whose first line is a real npm install for Codex. Everything after that line is base64. Bromure Agentic Coding never sends anyone looking for a coding agent: the install command is a fixed string in Bromure's own image catalog, it runs inside a disposable Linux VM, the credentials in there are decoys, and with unmatched outbound traffic denied the loader's three fetches never leave the machine.

For two years the industry has been building controls for what a coding agent installs. This campaign goes one step earlier and poisons how you install the coding agent. That step has no registry, no lockfile and no provenance attestation. It has a search box, a page that looks right, and your own hands on the keyboard.

A developer wants to try OpenAI's Codex CLI on their Mac, so they type codex macos download into Google. The first result is sponsored, sitting above OpenAI's own listing. It goes to a clean download portal with the right logo and the right typography, hosted on sites.google.com. The page says: open Terminal, paste this.

They paste it. The command starts with npm install and the package name is correct.

On August 24, Cato Networks' CTRL research team published the write-up of that campaign, picked up the same day by Infosecurity Magazine, SiliconANGLE and Techzine. The payload is a macOS infostealer. The delivery is what earns the write-up.

Google hosted the page. Someone else hosted the lure.

Google Sites is a site builder. Anyone with an account gets a page on a Google domain, and the certificate, the padlock and the first half of the URL all belong to Google. That is the rented trust.

The page itself contains nothing. Cato found no malicious code on it at all. The lure arrives in an iframe pointed at attacker infrastructure, routed, per Cato's analysis, through a Google static-content proxy, so even the second hop looks like Google fetching something. Techzine names two of the iframe hosts: bright-links[.]com and swiftsaverfin[.]com.

The split buys the operators two things. The visible page never changes, so it never trips a reputation system or gets re-reviewed, and they swap payloads on the far side of the iframe whenever they like. Cato counted three separate infrastructure sets, with the third reusing an iframe host from the first.

The rest reads like a mature ad-fraud operation. Live lures sit under an obfuscated path, /codexx/, while /codex/ serves something harmless. The operators fingerprint visitors, and anything that is not a plausible macOS target sees benign content, which is their answer to researchers and sandboxes. Domains rotate. Payload URLs vary.

The first line was real

ClickFix does not serve you a file. It gives you an instruction and lets you carry the payload across the boundary yourself. As Techzine puts it, "no file is downloaded that an antivirus solution could block."

The pasted command begins with a legitimate-looking npm install string for Codex. That is the whole trick. A developer who is about to install a Node CLI sees npm install, sees the package name they searched for, and their brain files the line as correct. The base64 blob that follows is one shell operator away, on the same line, past the fold of a copy box. You do not read the second half of a command whose first half is the command you expected.

Decoded, it fetches a remote script and pipes it into zsh. Three stages follow.

Stage one is a shell script padded with dead code and unused variables, wrapping an encoded blob that gets handed to eval. The padding exists to defeat pattern matching and to make a human skimming the file give up.

Stage two beacons back to the attacker with an event=pasted parameter before it does anything else. That is a delivery receipt. Someone on the other end is measuring paste-through rate on their landing pages, the way a marketer measures conversions.

Stage three downloads the binary to /tmp/helper, runs xattr -c to strip the quarantine attribute macOS attaches to anything that arrived from the network, which is what suppresses the Gatekeeper warning, marks it executable, and launches it. The binary is a universal Mach-O, so it runs on both Intel and Apple Silicon.

Cato notes the operators are still developing the loader. Earlier samples used base64 with compression; newer ones use AES-encrypted gzip containers whose key is reassembled from variables scattered through the script.

For the final payload, Cato ties the delivery framework to Atomic macOS Stealer: the loader URL structure, the shape of the telemetry request, the /tmp/helper staging path, the xattr removal, the update-themed payload URLs. They call the overlap strong and consistent and stop short of naming the binary, which is the right call and worth keeping when you repeat it.

Delivery: everything the victim seescodex macos downloadsponsored result, placedabove OpenAI's own listingsites.google.comCodex portal look-alike,no malicious code of its owniframe: the real lurebright-links[.]comswiftsaverfin[.]com"open Terminal,paste this"no downloadThe one line the developer readsnpm install ... codex ...; base64 -d ... | zshExecution: three stages, three outbound fetches1 · loadershell script padded withdead code and unused varseval "$blob"2 · receipttelemetry beacon home?event=pastedthe operator's conversion metric3 · payload/tmp/helperxattr -c ; chmod +xuniversal Mach-O
The chain from search box to running binary. No exploit anywhere in it: a sponsored ad, a Google-hosted page that contains nothing, an iframe that contains everything, and a human who carries the payload across the boundary by pasting it. The one command the victim ever sees begins with a real npm install line for Codex; the base64 that follows fetches stage one. Stage two's event=pasted beacon is the operator's own delivery receipt.

The step upstream of the scanners

Line up the controls the industry has spent two years building for coding agents. Registry reputation scanning. Lockfiles and integrity hashes. Build provenance and attestation. SBOMs. Minimum-age gates on new package versions. Static analysis on generated code. They all work, and they all start from the same assumption: the developer already has a working toolchain, and the risk is downstream, in what they do with it.

This campaign sits upstream of all of it. The counterfeit here is the agent itself, and it arrives before any registry, before any lockfile, in a command the developer types with their own hands into their own terminal. A scanner has no artifact to score, because the artifact is a sentence on a web page.

The targeting is the other half of the economics. Somebody bidding on codex macos download is buying the machine of a person who is, at that moment, setting up a coding agent: a machine that within the hour will hold an API token, an SSH key, a ~/.aws/credentials file and a GitHub PAT, all of which an infostealer knows how to find. The install moment is the cheapest time to be there.

We have covered the inverse of this before: with FakeGit and AgentBaiting, the agent does the searching and hands the human the install steps. Here the human does the searching. Both work because discovery-then-paste is how software gets installed, and no scanner sits at either end of that loop.

Where the install command comes from

Bromure Agentic Coding has no such loop, and the reason is where the agent comes from.

You do not go looking for a coding agent. Codex is a toggle in a profile's Agents panel, next to Claude Code and Grok Build. Switch it on, pick how it authenticates, and the agent is there in the next session, running in a kitty tab inside the profile's VM.

Behind that toggle is a fixed string. Bromure provisions its base image from its own catalog, and the entry for Codex is one line:

npx --yes @socketsecurity/cli npm install -g --silent @openai/codex

Claude Code is @anthropic-ai/claude-code on the same pattern, Kimi Code is @moonshot-ai/kimi-code. The package names are compiled into the app, not discovered. The registry is registry.npmjs.org, not whatever host an iframe nominates. The install is wrapped through the socket.dev CLI, and the fetch goes out through the host's MITM proxy, where the profile's Supply Chain policies apply: the two-day age gate that is on by default, plus the OSV lookups, socket.dev or Delpi filtering, and install-script stripping you can switch on.

That path has no search result and no page to impersonate. The channel the attacker paid to poison is missing from the workflow, and missing is not a control you have to remember to operate.

Install it the ordinary waywho names the artifact?whoever won the ad auctionwho serves it?an iframe on a look-alike pagewhat runs?whatever is past the first linewhere?your Mac, your user account, next to~/.ssh · ~/.aws · ~/.git-credentialsGatekeeper silenced by xattr -cFour open questions, answered by a stranger.Enable it in a Bromure profilewho names the artifact?@openai/codex, from the image catalogwho serves it?registry.npmjs.org, via the host proxywhat runs?one compiled-in command, no free textwhere?a disposable Linux VM, built beforethe search happened — no keys inside,credentials are decoys, egress is policedSame four questions, answered before you asked.
Two ways to acquire a coding agent. On the left, the acquisition step is a search box and a copy button: the name of the artifact, the host that serves it, and the command that installs it are all chosen by whoever won the ad auction, and the result runs on the Mac that holds the developer's keys. On the right, the artifact name is compiled into the app, the registry is fixed, the fetch is filtered at the host proxy, and the install runs inside a disposable VM that was built before any of this started.

Now paste it anyway

Architecture has to survive the day someone ignores it. Take the campaign, hand it the win, and walk it through a profile: someone finds the fake portal, copies the command, and pastes it into the terminal they do agent work in, which in Bromure Agentic Coding is a kitty tab inside the VM.

The binary does not run. A universal Mach-O is a macOS executable. The guest is Ubuntu. xattr is not a Linux command, and /tmp/helper is a path on a system disk that exists to be discarded. Resources → Storage gives you Erase home… for /home/ubuntu and Reset to base… to re-clone the workspace system disk from the shared, read-only base image.

The three fetches do not leave. Each stage of that chain needs the network: the base64-decoded URL that pulls stage one, the event=pasted beacon, and the payload download. Guardrails → Outbound connections is a pf-style rule table: action, protocol (tcp, udp, web, any), host or CIDR, ports, and for web a list of HTTP methods, matched top to bottom, first match wins, with Unmatched traffic set to Allow or Deny. Set it to Deny and list the hosts your work needs. bright-links.com is not one of them. Bromure enforces the table host-side, in the virtual switch and the proxy, so nothing the guest does to its own environment changes the answer.

Stage two is the one worth sitting with. event=pasted is the operator's conversion metric, the ping that tells them a landing page is working and how well. When that request dies at the host, they do not get a failed infection report. They get silence, which reads like an ad impression that never converted.

The theft returns placeholders. An Atomic-class stealer goes after browser data, wallets, and on a developer machine, credentials. In a Bromure profile the real values never enter the VM at all. Generic API keys are brm_… fakes exported as environment variables and swapped on the wire by the host proxy. The kubeconfig is synthetic, with throwaway client certs. AWS requests are re-signed host-side, so anything that routes around the proxy gets InvalidSignatureException. ~/.docker/config.json holds a fake base64 blob. Private SSH keys are not in there to read: they never were. Turn on Require approval to use per credential and each swap becomes a host dialog with a time-bounded grant: five minutes, an hour, the rest of the session. A profile shares at most eight folders you picked, so the rest of the Mac is not a directory walk away.

The correlation is already on your screen. Cato's own conclusion is that no single stage of this campaign exposes it; detection meant correlating the sponsored-search delivery, the embedded web content, the Terminal execution and the outbound activity together. That is a hard problem when your evidence sits in a browser, a shell and a network tap. In a profile it is one window: Security Log (Window → Security Log…) is a single chronological host-side table of package verdicts, firewall decisions, credential swaps and prompt-injection detections. Guest code cannot edit it, because guest code cannot reach it.

Turn it on

The age gate is on by default at two days. The other layers are settings, and they are worth two minutes: Guardrails → Outbound connections, with Unmatched traffic set to Deny and an allow list for the hosts your work needs. Supply Chain → OSV vulnerability check, plus socket.dev or Delpi filtering if you have a key, and install-script stripping. Credentials → Require approval to use on anything that can spend money or delete data. Then leave the Security Log window open on a second display for a week and see what your builds talk to.

Sponsored search results are going to keep being the front door, and site builders on trusted domains are going to keep hosting the porch. Google will take these pages down and three more will go up, because the economics are a few dollars per click against a developer workstation with cloud credentials on it. Install Bromure Agentic Coding, enable your agents from the profile editor instead of a download page, and let the next pasted command land in a machine that was built to be thrown away.