Back to all posts
Published on · by Renaud Deraison

The command was called aipoison

Socket found a cluster of 18 npm packages impersonating Alibaba's private @ali scope. None of the tarballs contained malware. The RAT assembled itself at install time out of a JSON file in a GitHub repo the attacker still controls, and its persistence plan included patching Python inside the .skills directories of AI coding tools, using commands named aipoison, aipoison_inject, and aipoison_deploy. Reading the code could not have found this. Bromure Agentic Coding judges what was visible all along: a package with no reputation pauses for your consent at the host proxy, the three fetches that assemble the payload hit a wire that only speaks to destinations you approved, and every file the RAT writes lands in a VM that lives for one task.

Somewhere in the final payload is a command named aipoison. Its job is to reach into the skills directory of your AI coding tool and patch a Python file, so that the next time you start work, the attacker starts too.

On August 3, 2026, The Hacker News covered research Socket published in late July: a cluster of about eighteen npm packages, staged in late April, that sat on the registry for three months before anyone caught them. They impersonate Alibaba's private @ali-scoped packages by publishing the same names unscoped (aone-cloud-cli, def-open-client, open-worker-cli, uniapi-bridge), so a developer or a build inside the target organization resolves the public lookalike and gets a remote access trojan grafted onto the dependency tree. Socket's researcher Karlo Zanki reads the motive as industrial espionage.

Nothing in the packages was malicious

Open any one of those tarballs and you find nothing to report. There is no postinstall hook, the first thing anyone greps for. The lure packages depend on smart-config-manager, which depends on cloud-config-fetcher and local-config-parser. A config manager, a fetcher, a parser: boring utilities, each legible in a minute.

The malice lives in the composition, and in a file the attacker never shipped. On import, the fetcher downloads defaults/preferences.json from a GitHub repository the attacker controls, and still controls, then writes it to disk as .cloud-preferences.json. The parser evaluates that file's "transform rules" inside a Node vm, which is where the attack lives. One rule is a sandbox escape with folklore status:

var F = items.constructor.constructor;   // recover Function
var p = F('return process')();           // recover process

From process it walks a fallback ladder (getBuiltinModule, mainModule.require, mainModule.constructor._load) until one of them yields require on whatever Node version it landed on. Then it fetches setting.js from an Alibaba Cloud storage bucket, which fingerprints the host and installs the payload: on Windows, kill Alibaba's own Alilang security app and replace its app.asar with a trojaned copy; on Linux, drop a binary in /tmp, run it detached, delete the file; on macOS, inject into ~/.zshrc and register a LaunchAgent that fires every ten minutes.

GBHackers put the design in its headline: attackers split the RAT across packages to evade isolated code reviews. The attacker built this campaign to survive someone reading it. Review any package on its own and it passes. Review the whole tree and it still passes, because the hostile part is a JSON file on a server, fetched after you finished reviewing, editable by the attacker between your audit and your install.

The persistence plan has your agent in it

Socket recovered the final payload as aone-cli. It does what these things do: shell commands, file upload and download, host recon, screenshots, an encrypted reverse TCP proxy, and a dws_lateral command that moves through DingTalk. Its C2 traffic wears forged Origin and Referer headers claiming to come from an internal Alibaba docs domain.

One group of commands makes this a coding-agent story. Alongside the usual verbs, the RAT ships aipoison, aipoison_inject, and aipoison_deploy. They patch Python scripts sitting in the .skills directories of specific developer tools, marking the injected snippet with # __INJECT_MARKER__, so a script pulled from C2 runs later, from inside the tooling, as you. Among the named targets is Qoder, Alibaba's own agentic IDE, which passed five million users within months of launching last August.

Someone sat down, looked at the modern developer machine, and picked the agent's skills folder as the durable foothold: a directory of scripts that runs with your permissions at the start of every session, and that few people ever diff. We have watched this move before, when a worm copied itself into .claude so the next agent start would reinfect the machine. It has graduated from a worm's improvised trick to a named command in an espionage toolkit.

The parts the attacker could not hide

Reading the code was never going to find this. The attack still had requirements, and all of them were visible: a package with no history and no reputation, three outbound connections before it could act (the GitHub rule file, the cloud-storage stage, the C2), and a machine that would still be there tomorrow.

Bromure Agentic Coding puts a decision on each of those, and makes it on your Mac, outside the box the agent runs in, where nothing inside can argue.

The install pauses at the registry. Every package fetch crosses the host-side proxy, which applies your workspace's supply-chain policy to the response before a byte reaches the VM. Turn on package filtering and the proxy consults a reputation provider on each artifact. One of the two selectable providers is Socket, whose research this post is about. When no enabled source can vet a package, the proxy holds the download and asks you. Lookalikes of a private scope, published last quarter with a handful of downloads and no reputation anywhere, stop there. Enforcement sits on the host by design: the .npmrc inside the VM can tighten the policy and cannot loosen it, so the rule holds even when the compromised thing is the thing doing the policing.

The wire refuses the assembly. This design breaks against egress consent, because with nothing hostile in the tarball, the network is the attack. The host proxy checks each of the three fetches (the attacker's GitHub raw file, the storage bucket serving setting.js, the C2 host) against the destinations you approved for this profile. The registry you install from is on that list. A stranger's raw-content path and a bucket named after a CLI you have never heard of are not. The proxy has no opinion about items.constructor.constructor and needs none. It reads the destination, finds nothing approved, and drops the connection. Your install finishes, your build runs, and the RAT never assembles, because its parts never reach the same room.

Persistence gets a machine with a lifespan of one task. Suppose the whole chain runs anyway, because you approved a destination wider than you meant to and the stages land. The ~/.zshrc line, the ten-minute LaunchAgent, the trojaned app.asar, the patched .skills Python: all of it writes to a disposable Linux VM, one hypervisor away from macOS, holding this task's workspace, and you throw the VM away when the work merges. aipoison is a bet that the tooling it patches and the host it compromised are the same long-lived machine. In a profile, that bet loses. The recon that runs first comes back with broker decoys: your real credentials live on the host and get injected at the wire only for approved destinations, so they were never in the VM to collect.

How the RAT assembles: nothing hostile is in the package1 · npm installtarballs: cleanno postinstall hook3 utility packages2 · preferences.jsonfrom attacker's GitHubvm escape in a "rule"editable after your audit3 · setting.jsfrom cloud storagepicks the OS payload.zshrc · LaunchAgent4 · C2shell · files · proxyaipoison → .skillspatches your AI toolingSteps 2, 3 and 4 are network events. The code you could read only performed step 1.The same four steps, inside a Bromure profileDisposable Linux VMagent · npm · anything the payload writes.zshrc, LaunchAgent, .skills → this machine onlycredentials in reach: broker decoysdiscarded when the task endsevery hopHost proxy, on your Mac1 · registry → reputation check, or ask you2 · attacker's GitHub raw file → refused3 · unknown storage bucket → refused4 · C2 → refused
The attack assembles itself out of three fetches. Nothing in the npm tarballs is malicious: the hostile logic arrives from a GitHub file the attacker still controls, the stages arrive from cloud storage, and the RAT then calls home. In a Bromure profile the host proxy checks every one of those hops against the destinations you approved for this workspace, and every file the payload writes lands in a VM that exists for one task.

Reviewing an artifact tells you about the artifact

Researchers keep re-teaching this lesson in a different library every week. Yesterday a trust flag fell because the check and the load happened at different moments. In June, a repository stayed clean because the reverse shell lived in a DNS record. Now eighteen packages read clean because the malicious part is a JSON file the attacker is still holding.

Agents sharpen this, because they are fast. An agent asked to wire up an internal API adds the dependency, runs the install, and moves to the next step of the task in the time it takes you to switch windows. That speed is the whole reason you bought it. Put the judgment where the speed cannot outrun it: at the registry fetch and at the wire, where your Mac decides against a policy you set once, for every package and every connection the agent makes.

Keep letting the agent install things, because that is where the leverage is. Install Bromure Agentic Coding, turn on the supply-chain layers, and give the profile a list of destinations that make sense for the work. The next campaign will hide its payload somewhere else, the one safe prediction in this business, and it will still have to fetch it across a wire that asks you first.