Back to all posts
Published on · by Renaud Deraison

The agent had an address

At DEF CON 34, Team Cymru's Eli Woodward showed 48 hours of capture from an AI honeypot: 3,993 requests, 327 source IPs, 344 credential probes, against ports no one had advertised. The other attacks on a coding agent need a way to put text in front of it. This one needs a route to it. Bromure Agentic Coding runs the agent behind NAT by default, so there is no route to find.

The other attacks on a coding agent need a way to get text in front of it. This one needs a route to it, and the scanners already have one.

Saturday morning at DEF CON 34, on Creator Stage 3, Eli Woodward of Team Cymru gave a half-hour talk called MCPwned: How Exposed AI Agents Became the Internet's New Recon Toy. He had built an AI honeypot: 16 LLM and AI-infrastructure personas across 16 ports, each returning framework-authentic responses, headers, errors, and protocol behavior, so a scanner touching it would believe it had found the real thing.

In one 48-hour window the honeypot took 3,993 requests from 327 unique source IPs, including 155 MCP probes and 344 AI API-key probes.

No one had advertised those ports, and none of the 327 addresses had singled Woodward out. The playbook he pulled from the capture is the boring, industrial kind: LiteLLM model-registration abuse, MCP resource enumeration, framework-aware credential brute forcing, and coordinated scanning for exposed local inference services. The crews who have spent a decade sweeping the address space for open RDP and unauthenticated Redis have pointed the same machinery at AI developer tooling, ahead of the threat models.

Nobody has to send you anything

Read back through the last year of coding-agent attacks and they share a shape. Something the agent reads has instructions in it: a bug report, a hidden PR comment, a web page the agent fetched, a README, a WAF log row. In each of those, delivery is the attacker's hard problem. They have to find a channel you connected on purpose and get their text into it.

Flip the arrow and that problem goes away. A scanner sends a SYN to port 11434, and something on your machine answers.

What the scanners find

The population behind Woodward's capture runs to five figures.

Censys mapped internet-accessible MCP services and found 12,520 of them across 8,758 unique IP addresses in 56 countries and 425 autonomous systems as of April 28, 2026. A week later, on May 6, the count was past 21,000. Roughly four in ten accept requests from anyone, with no credential check, because the protocol did not require one by default.

Wiz went and knocked. In a survey published July 28, they found MCP present in 80% of cloud environments, with about one in six exposing at least one server. Of the exposed ones, around 70% handed over their complete tool catalog to an unauthenticated caller, about 42% returned real data when the researchers invoked a tool, and roughly 10% fronted sensitive backends. Wiz pointed a confirmed subset at cloud metadata endpoints and got temporary credentials back.

Knostic ran the same exercise from Shodan, fingerprinting 1,862 exposed servers; every one of the 119 they verified by hand answered tools/list without authentication. Each of those servers was doing the one thing it exists to do, for a stranger.

Local inference is the same picture with rounder numbers. LeakIX counted 12,269 unauthenticated Ollama instances on February 23, 2026, each one offering model enumeration through /api/tags, free compute through /api/generate and /api/chat, model theft through /api/pull and /api/push, and in about a thousand cases the Probllama path traversal chain.

48 hours on 16 ports that nobody advertisedthe internet, scanningno target list, no phishing,nothing to deliverjust a SYN to a portsomething answers16 LLM and AI-infra personason 16 ports, framework-accurateheaders, errors, protocoland the log fills up3,993 requestsfrom 327 unique source IPsin a single 48-hour windowthe playbook, unchanged from one IP to the next155 MCP probes344 AI API-key probesLiteLLM model registrationlocal inference sweepsresource enumeration, framework-aware credential brute forcing, and whatever answered last timeWhat the scanners are finding, on the live internet21,000+ MCP servicesCensys, May 2026, up from 12,520roughly 4 in 10 take no credential70% hand over the catalogWiz, July 2026, anonymously42% return real data when invoked12,269 open OllamasLeakIX, February 2026enumerate, generate, pull, push
Woodward's 48-hour capture, and the live population behind it. The scanner needs no target list and no delivery channel, because the services answer for themselves.

How your machine ends up answering

You did not decide to put a service on the internet. You set OLLAMA_HOST=0.0.0.0 so the laptop on the couch could use the model on the desktop, and you bound an MCP server to all interfaces for the same reason. You ran docker run -p 8080:8080 because the README said to. You opened a tunnel to debug something on a phone and left it up.

Then you carried the laptop somewhere. Conference wifi, a coworking floor, a hotel, a home router where UPnP obliged on your behalf, or a cloud dev box whose security group somebody widened in a hurry. The bind is one flag; the exposure belongs to whoever wrote the routing table you happen to be sitting behind. At no point did you agree to it.

Bromure Agentic Coding closes that gap by default, without you setting anything.

The VM does not have an address

Bromure runs the coding agent in a disposable Linux VM on Apple Silicon, with every byte of its traffic crossing a proxy on the host, outside the box the agent runs in. For this attack class, the network side of that arrangement carries the weight.

Under Resources → Network, the mode is NAT, and NAT is what you get without making a choice. The VM shares your Mac's network connection through Virtualization.framework's built-in NAT: egress works, and nothing on your LAN can reach the VM. Bind a service to 0.0.0.0 inside that VM and you have bound it to a VM-internal interface behind the NAT. There is no port-forwarding table to fill in wrong, because the mode does not forward ports at all.

The scanner's SYN never reaches a port at all, filtered or closed, because no route leads to one. Woodward's honeypot needed 16 listeners to collect anything. A Bromure profile presents none of them, and the 344 credential probes have nowhere to land.

Bridged is there when you want it, and it hands the VM a LAN-routable address by DHCP on a physical interface you name. It is a per-profile setting with an explicit interface picker, so you end up on the LAN on the run where you meant to be on the LAN.

Your MCP servers do not listen

The servers in the MCP panel are profile configuration. Bromure translates them into Claude Code JSON or Codex TOML and injects them at boot. A stdio server is a child process inside the VM speaking over pipes, so it has no socket and tools/list enumeration has nothing to reach. An HTTP server is a URL you point at, with an optional bearer token, which makes you the one sending the request. The 40% of MCP services that answer strangers are servers somebody stood up and bound by hand.

Local inference runs on the Mac

"Coordinated scanning for exposed local inference services" is the honeypot's own phrase, and 12,269 open Ollamas is what it finds. Claude Code's Local model authentication routes inference to an on-device MLX model chosen in the Local Models panel, which runs on macOS, on the host side of the proxy. There is no port 11434 inside the VM to expose. The on-device leg of Fusion takes the same path.

A probe that got in would collect placeholders

Anyone running framework-aware credential brute forcing knows where to look: OPENAI_API_KEY in the environment, ~/.aws/credentials, ~/.docker/config.json, a kubeconfig, ~/.ssh. Bromure puts all of those in the VM and makes all of them fake. You get brm_… and xai-brm-… tokens, a synthetic kubeconfig with throwaway client certs, a fake base64 registry auth, and AWS material that returns InvalidSignatureException the moment anything bypasses the proxy. Bromure keeps the real values on the host and substitutes them on the wire for the destination they belong to.

Destructive verbs get decided on the host

Wiz found roughly 42% of exposed servers returning real data, and a write and delete class alongside it. Guardrails is a policy engine in the host proxy covering Kubernetes, AWS, DigitalOcean, container registries, GitHub, GitLab, Bitbucket and the HTTPS database endpoints. Set Block destructive or Read-only per resource and the blocked call comes back as a hard 403 the agent reports as an ordinary API failure. Bromure makes that call in macOS, so there is nothing inside the VM to switch off.

On the developer's laptopOLLAMA_HOST=0.0.0.0so the other laptop can use the modelport 11434, listening on everythingthe MCP server is bound the same waytools/list answers whoever asksthe catalog is the reconnaissanceand the env is real344 key probes in 48 hours know the pathswhoever routes to you decidesconference wifi, UPnP, a wide security groupInside a Bromure profilethe VM is on NAT, by defaultegress works; nothing reaches in0.0.0.0 binds to a VM-internal interfaceMCP over stdio is a pipea child process, no socket to enumerateHTTP servers are URLs you point atlocal inference runs on the Macno 11434 in the VM to scan forand the env holds brm_… anywayreal values on the host, swapped on the wire
The same two conveniences, on two machines. On a laptop, one flag each puts a service on whatever network you happen to be sitting on. In a Bromure profile the VM has no inbound route, the MCP server is a pipe, the model runs on the Mac, and the environment a probe would sweep holds placeholders.

A task from a stranger is still text

Suppose you connect a remote MCP server that someone else exposed, and it answers you with more than data. Once scanners can enumerate these services, whoever finds one can also use it to reach your agent through the front door, as content.

Bromure scores that content on the way in. A local prompt-injection model reads the file contents, web pages, and tool_result blocks the agent takes in, looking for an imperative aimed at whoever is reading. Log but continue records each hit in the Security Log. Ask me what to do pauses the outbound request and puts the flagged span in front of you. Block unilaterally stops it before the model sees it and hands the agent a hard 451. A second detector, a fine-tuned ModernBERT classifier plus a deterministic scanner for zero-width, bidirectional and Unicode-tag payloads, covers CLAUDE.md, AGENTS.md, GROK.md and their nested and global variants.

Both models run on your Mac, downloaded once from bromure.io. Scoring the output of a third-party MCP server does not involve sending that output anywhere.

The knock is a row in the log

Every request the VM makes crosses the host proxy, and at the Activity trace level Bromure records the host, the status, the latency, the swap report, and any leak warnings for each one. Injection detections land in the Security Log with the flagged text attached. Raise the level and Bromure keeps the AI request bodies too, encrypted with the same keychain key as your profile secrets.

An agent that started taking instructions from outside has to reach somewhere to be useful to anyone, and reaching somewhere is a line you can read on the machine the session ran on.

And when you want the machine gone, it goes. Erase home wipes /home/ubuntu, meaning dotfiles, .ssh, npm-global, .cargo and shell history, back to its post-clone state. Reset to base discards every system-level change and re-clones the workspace disk from the base image.

MCP will grow better defaults and Ollama will grow an auth story, and the ports in Woodward's honeypot will be different ports by next August. The shape holds. Running an agent well now means running a gateway and a handful of tool servers beside it, and each of those is a program that binds a socket on a laptop that travels.

Other posts here ask you to distrust what your agent reads. This one asks a shorter question: what answers when a stranger knocks, and would you rather nothing did.

Install Bromure Agentic Coding, and give your agent a machine the scanners cannot see.