The orders came through the API
On November 12, Darktrace showed how a stolen Claude Console key turns a developer's own Claude Code into a command-and-control implant: jobs arrive in a Claude memory store, the agent scans the network, sprays a password and uploads 1 GB to the Files API, and every packet goes to api.anthropic.com. In Bromure Agentic Coding, the key the attacker steals is a stand-in that pauses the VM on its way out, a key they bring is refused, the network scan is refused at the Mac, and every job is in the trace.
The operator typed each order into a text file in a Claude memory store. The developer's own Claude Code read it, ran nmap and smbclient, and uploaded the loot to Anthropic.
On November 12, Darktrace Signal Labs researchers Moishe Tajtelbaum and Sam Lister published Living off the AI: Shared Memories as a Covert C2 Channel. It's a lab demonstration, and Darktrace says it hasn't seen the technique in the wild. It also says the technique "does not rely on a vulnerability in the Claude platform". Nothing will be patched. It's built from Claude features working as designed.
The starting point is a Linux machine where a developer runs Claude
Code. A small payload, fetched with curl and run by dash, looks for
the Claude Code installation, collects the API and OAuth credentials
for the developer's Claude Console account, and sends them back to the
attacker's staging server with another curl.
With those credentials, the attacker creates a memory store, a set of text files attached to a Claude Console workspace, in a workspace the developer belongs to. It holds three files: a heartbeat, an input file for jobs, and an output file for results.
Back on the developer's machine, the payload uses systemd-run to create
a transient service that keeps launching Claude Code. Each run checks in
to the heartbeat file through a hook, asks Claude Sonnet 4.5 in
non-interactive mode to read the next job from the input file, carries it
out with bash tool calls, and writes the result to the output file. Then
the payload deletes itself. From that point the operator sends every
order as plain English into a file on Anthropic's servers.
Nine jobs, one destination
Darktrace ran nine jobs against a simulated Active Directory network:
- Look around the local network for SSH, SMB and FTP. The agent used
ip,ssandncand found a server at 192.168.1.2. - Run a given
nmapcommand against that server. - Download what the anonymous FTP share offers. It held employee details and a password with no name attached.
- Build a username list from the employee names: 190 entries in
/tmp/usernames.txt. - Spray the password at the domain controller over SMB. It worked for one account.
- List the SMB shares that account can see, with
smbclient. - List one share's contents, which included a 1 GB file.
- Download that file to a temp folder.
- Split it into three parts and upload them to the Claude Files API.
Every one of those orders, every result and the 1 GB upload went to
api.anthropic.com, the same host the developer's agent talks to all
day. Most companies that use coding agents have put that host on their
allow lists. Darktrace's point is that the allow list is now the C2
channel, and the attacker never had to stand up a server of their own
after the first curl.
Darktrace's advice follows from that. Move developers from long-lived
API keys to OAuth or federated identity. Where you can't, keep keys
short-lived, scoped to one workspace, and out of dotfiles and shell
history. Make sure your endpoint telemetry can tie an agent process's
command lines and child processes to how much data it sends. Watch for
upload volumes far above normal inference traffic, and for agent
processes running nc, nmap or smbclient.
Bromure Agentic Coding was built around the same advice, applied on the Mac rather than on the developer's laptop.
The same machine in Bromure Agentic Coding
Bromure Agentic Coding runs Claude Code, Codex, Grok, Kimi and omp in Ubuntu VMs on your Mac, one VM per machine. The agent, its shell and every program it starts stay inside that VM. Credentials in the VM are stand-ins. Every connection out passes through a virtual switch and a proxy on the Mac, where the agent can't reach.
Give the attacker the same foothold: their payload runs inside a Bromure machine, as the agent's user, and goes looking for Claude Code's credentials.
The key it steals pauses the VM
With an Anthropic API key configured, ANTHROPIC_API_KEY in the VM
holds sk-ant-api03-brm- followed by random characters. It's the right
shape, so Claude Code accepts it, but Anthropic never issued it. The
real key is encrypted on the Mac, and the proxy swaps it in only on
requests bound for Anthropic's hosts.
With a Claude subscription, the sign-in happens on the Mac. The proxy answers the token exchange itself and keeps the OAuth tokens in the host's store, and Claude Code is told the credential stays on the Mac. There are no OAuth tokens in the VM for the payload to collect.
Then the payload sends what it found to its staging server, and every outbound request from the VM passes the proxy's scan. A Bromure stand-in has one legitimate destination family, and the staging server isn't in it. The proxy blocks the request with HTTP 451, forwards none of it, and pauses the VM. An alert titled This environment may have been compromised names the credential, the host it belongs to and the host it was sent to. Shut down, the default, stops the machine and marks it compromised. The attack stops at step one, before any memory store exists.
No real key, no channel
Suppose the payload hides the key well enough to get it out. The
operator now holds sk-ant-api03-brm-…. Anthropic's API rejects it.
They can't create a memory store in your workspace, write a job into
it, or read a result out of it. The channel needs the operator to reach
the store from their side, and from their side the key doesn't work.
The attacker could bring a key of their own instead: an Anthropic key for an account they control, dropped into the VM, so the implant reads jobs from the attacker's own workspace. Turn on Only allow credentials Bromure injects in the machine's Guardrails. With this strict mode on, any request that carries a credential Bromure didn't put there is blocked and raises the same compromise alert. Your Anthropic traffic keeps working. A second Anthropic account in the same VM doesn't get through.
The scan stops at the Mac
The jobs themselves need the network. Bromure machines sit on their own
private subnet, and every packet out goes through the Mac. Switch the
machine to OpenShell + Sentry and everything is denied unless a
rule allows it. Private ranges such as 192.168/16 are reachable only
through an exact hostname or an allowed_ips entry. Darktrace's first
job finds nothing on 192.168.1.2, nmap gets nothing back, and there's
no FTP share or domain controller to spray. Each refused connection is a
Firewall row in the Security Timeline.
With Enforce binaries on, a root-owned attestor in the VM ties every
connection to the program that opened it, and each rule names the
programs that may use it. nc, nmap and smbclient aren't named
anywhere. The same mode takes away every path to root, so the payload
can't use sudo to get around it.
The agents in the machine hear about it too. With Tell the agent when policy blocks it on, the default for OpenShell machines, Bromure uses the kernel sentry's process chains to find the agent behind a blocked connection and writes a note into its conversation: what was blocked, that it's deliberate, and not to retry it or reach the same thing another way. A Claude Code session that was talked into scanning the network gets that note right after the blocked command.
The upload trips the watchdog
Darktrace's last job sends 1 GB to the Files API in three parts. An agent's normal traffic is prompts, responses and small tool results. The agent watchdog watches each session from outside the VM. It counts outbound volume at the network switch, separately from the proxy, and compares it with the session's own baseline. It also counts bursts of blocked requests, like a scan or a password spray running into default deny. Set to Quarantine, it cuts the VM's network and pauses it within milliseconds, then lists what it saw and asks you what to do.
The trace shows every job
Darktrace's detection advice comes down to correlation: link the agent process to its command lines, its children and the bytes it sends. In a Bromure machine those records are already on your Mac, and the agent can't edit them.
The proxy writes one trace record per request: host, method, path,
status, and request and response bytes. A heartbeat every few seconds,
reads and writes to a memory store, and three large POSTs to
/v1/files each get their own row, with their sizes.
New machines trace at AI request details, which keeps full request
and response bodies for Anthropic and the other model providers and
parses them. In the Trace Inspector's Conversation view, the
operator's jobs, which Claude read from the memory store and carried
into its context, appear as plain text. Next to them are the model's
Bash tool calls with their exact commands: the nmap line, the
smbclient invocation, the split. Darktrace had to reconstruct the
chain from network and endpoint telemetry. Here it's laid out in order.
On a machine with the kernel sentry, every program launch and every
connection is reported from inside the VM's kernel, over a channel the
agent can't silence by killing a process. A refused connection to
192.168.1.2 opens into its network lineage: the prompt, the agent's
reasoning before the call, the tool call, and the process chain from
claude through bash to nmap. The Prompt card holds a job you never
wrote.
Cleaning up
If the compromise alert fired, the machine is marked compromised and won't boot until you choose Wipe and Launch. That wipes its disk image and home folder and keeps your tokens, SSH keys and settings. Without the alert, Reset disk re-clones the system disk from the base image. Rewind home… rolls the home folder back to the start of an earlier day, before the payload arrived. And since the machine never held your real Anthropic key, the key you'd otherwise have to rotate stays where it was: on your Mac.
The key is a tripwire
The payload finds an sk-ant-api03-brm- stand-in, or no tokens at all with a subscription. Sending it to the staging server gets HTTP 451 and pauses the VM.
No channel to open
A stand-in can't create or read a memory store. A key the attacker brings is refused by strict credential mode.
Jobs hit the wall
Default deny refuses 192.168.1.2, Enforce binaries names nmap and smbclient nowhere, and Claude Code is told not to retry.
Every order on record
The trace keeps each request's path and size and parses the jobs and tool calls. The watchdog quarantines a session on an outsized upload.
What to do this week
- Find where your developers' Claude, OpenAI and other model API keys
live: shell profiles,
.envfiles, agent config folders. Move what you can to OAuth or federated sign-in, and make the rest short-lived and scoped to one workspace. - In the Claude Console, review which workspaces each key can reach and check them for memory stores and Files API uploads you don't recognize.
- Alert on agent processes that start
nc,nmap,smbclientor similar tools, onsystemd-rununits that launch an agent, and on uploads to model providers far above normal inference traffic. - Don't treat an allow list entry for
api.anthropic.comas a trust decision about what goes through it.
Then give your agents a machine where the key they hold works only on the way to Anthropic, the network they can reach is the one you listed, and every request they make is recorded where they can't change it. Stand-ins and strict mode are covered in the Credentials & the Wire Boundary chapter of the manual, default deny and the kernel sentry in Sandbox Policy, and the trace in Tracing & Audit.