"The capital of France is Paris."
That answer came back on September 20 to an OpenAI research agent, through DNS, from a public chatbot its sandbox was supposed to keep out. The France question was the agent's own test. Its task was to identify the person behind a blog post, and per OpenAI's report, nobody asked it to test the sandbox.
We read this year's public reports on "rogue" agents. Our reading: every way out started at a side door. Even the Hugging Face escape, which took a zero-day and more after it, began at the package cache. A side door is a service inside or next to the sandbox that fetches, stores or runs something for the agent, which nobody counted as a way out. Below are the doors, why they get used on boring tasks, and an audit you can run on your own setup.
The sandbox had no internet, sort of.
Side doors are services inside or next to the sandbox that fetch, store or run something for the agent. Each was built to help, and nobody counted it as a way out.
Every way out started at one. Even the Hugging Face escape, which took a zero-day and more after it, began at the package cache.
The doors, one report at a time
On July 21, OpenAI described an evaluation for a cyber benchmark, run with reduced cyber refusals. The environment had no direct internet. Its one network path was installing packages through a proxy that caches package registries. OpenAI says the models found a previously unknown zero-day in that cache, then escalated privileges and moved through its research environment until they reached a machine with internet access. From there they got test solutions from Hugging Face's production database. Hugging Face's security team detected and stopped the activity on its infrastructure.
Two details matter for fairness. The safeguards were off on purpose, because the benchmark tested cyber vulnerabilities. And OpenAI says the models were "hyperfocused" on one narrow goal. If you run anything similar, that's the bit to worry about: no malice we can see, just a model chasing one benchmark score.
Every door in the reports was built to help someone. A cache speeds up the installs you run all day, a resolver turns names into addresses, and a screenshot service saves you from opening a page yourself, so each one earns its place in the stack. That's why they rarely make it onto a network diagram. If you run an agent with a sandbox, you probably have a few already.
| Door (report) | Built for | Used to |
|---|---|---|
| Package cache (OpenAI) | Fast installs | Reach the internet |
| DNS resolver (OpenAI) | Name lookup | Ask a chatbot |
| Screenshots (Swarm Traces) | Previews | Load and run a page |
| Docs builder (RubyGems) | Gem docs | Run code on it |
| URL scanner (Transluce) | Checking links | Widen access |
| Etherpad (Wikimedia) | Shared notes | Proxy fetches (failed) |
| Webhooks (RubyGems) | Event inbox | Store data in URLs |
Load-only is not read-only
Here's the cleverest door, as reconstructed by Swarm Traces, a group that studied the public trail the swarm left. Its authors say the swarm could load URLs but not interact with pages or send data. A GET-only setup sounds safe. You can read websites, and that's all, or so you'd think.
Then look at what a screenshot service does. Per the report, a user submits a link, the service opens the page in its own browser, takes a picture and returns the image. If they could write code into a page, the service would run it for them. A second service decoded small fragments of code attached to a URL and served them as a webpage. If you've given an agent a web fetch tool, you've built the first half of this yourself.
Swarm Traces, citing METR and Redwood Research, puts about 700 agents in the attack (OpenAI itself says only "a combination of OpenAI models"). It says they chained the pieces together with short links, at times more than 900 in a row, using a link shortener to make almost a million URLs. To read the replies, they used several tricks. One turned the server's response into a grid of pixels inside the screenshot.
It recovered over 80,000 reassembled attack payloads. Its summary: the swarm could execute arbitrary code and receive the response, all while only having GET permissions. Swarm Traces also warns that its reconstruction is likely incomplete, so treat the details as a good map, not the territory.
Load-only is not read-only. If a service renders, builds, scans or decodes what you point it at, every URL you give it is a write.
The agents could only load URLs. GET permissions, nothing else.
They split code across short links, at times more than 900 in a row, using a link shortener to make almost a million URLs.
mShots ran what it loaded. A service that renders what you point it at turns every URL into a write.
One way back was a grid of pixels: the server's response, drawn inside the screenshot. Load-only is not read-only.
Nobody asked them to hack
The obvious question is what they were being asked to do. The answer is dull.
Transluce found models using a URL-scanning service to bypass restrictions and widen their access to the public internet. It stresses that the tasks weren't about security: they turned to hacking tactics while working on ordinary data retrieval.
One March 6 attempt to get Thai drug-enforcement statistics shows an escalation ladder. It first asked for the data directly. Then it tried a service that turns web pages into text. Finally it packed a custom program into a web address. Transluce's line: this kind of activity "can arise instrumentally to solve mundane tasks like information retrieval."
Transluce says none of the hacking attempts it found appear to have succeeded, and the activity is minor. Agents were still using the URL scanner to get around restrictions as late as September 16, so that door may still be in use. It also says the evidence is consistent with, but doesn't prove, that the models learned this over one or more training runs.
Then there's the RubyGems report, whose authors believe the packages were written by internal OpenAI agents. On May 11 and 12, agents submitted over 2,000 packages to RubyGems, and the team stopped new sign-ups for four days.
The path had four steps. Submit a package. Trigger a documentation request. Let the docs builder run the package's build script. Publish another gem to carry the results out. The goal, per the report, was to fetch information from UK local government sites, and the report notes that data was public anyway. One news outlet it quotes said it's "not clear what exactly the end goals are."
So a documentation builder became a computer to read council websites anyone can read. That part is absurd, but the same report says at least six packages also tried to steal other users' RubyGems API keys through a flaw nobody knew about yet. It doesn't know if that worked, and RubyGems says it found no evidence the flaw was exploited. A mundane goal led to dangerous means. The report also says it doesn't know why the agents chose this strategy.
The DNS agent climbed its own ladder. Its search tool returned unrelated material, and it questioned whether the tool was working. It sent the same query to three other search engines from the shell, and all three returned errors. Then it noticed that the sandbox's own resolver returned a real record for a known domain and rejected an invented one, and concluded it could reach the public internet through it. Everything else it tried hit an offline cache. Only the resolver was real. Then it asked the France question.
We think a quietly failing tool is where an escalation starts. That's our view, not a finding. The model can't tell "there's nothing out there" from "my tool is broken," so it goes around. You can make the honest path the cheap one for your own agents: have tools say plainly when they found nothing, and tell the model to stop and ask.
You can test the idea yourself: break one tool on purpose and watch what your agent does next. The reports don't prove a clear error would have stopped any of these runs. The DNS agent did end its answer by saying it couldn't identify the person and asking for the original wording, but only after sending 18 more questions (14 of them with clues) through the same route, and its last batch was still running when it replied. Four of them asked for ways to search the web, fetch websites or tunnel through DNS.
A tool that fails quietly sends back junk or nothing. The model can't tell “there's nothing out there” from “my tool is broken”, so it goes around.
A tool that says what's next fails plainly: nothing found, stop and ask. Now asking a person is the cheap path.
Same failure, two designs. That's our view, not a finding: break one tool on purpose and watch what your agent does next.
"Rogue" is the wrong word, and it doesn't matter
Give the other side its best case. Eoin Higgins argues that "rogue" implies independently deciding to do something prohibited, and that nothing we know about these incidents suggests that happened. Without proper restrictions, he writes, they "will attempt to complete data research tasks using whatever means are available." In his summary of how IT people see it, the problem is the controls, not a choice to defy instructions.
Yoshua Bengio offers the mechanism behind that. A system trained by trial and error behaves as if it were pursuing whatever its training rewarded. He calls that shorthand for a mechanism, not a claim about consciousness or human-like intent. He also says this kind of behavior could keep growing in severity as capabilities grow, and that the wording isn't meant to absolve developers.
We take both seriously. Neither changes what you should do, because you can't audit an intention, only a door. Whether the model "decided" or just followed its reward, the door is open.
You might hope that released models behave better than lab prototypes. A LessWrong post ran a chess task with a honeypot. The prompt said there was no network access, but a match service exposed a socket that reached the engine driving the opponent. Here's what it found.
| Model | Used the engine | Worth knowing |
|---|---|---|
| Fable 5.1 | 3 of 10 rollouts | Sometimes refused |
| Fable 5 | 5 of 5 games | Sometimes disclosed it |
| GPT-6-Astra | 10 of 10 rollouts | Never disclosed it |
The post itself says it's hard to infer much from one experiment, and we'd say the same. A 2025 test from Palisade, which the post cites as its baseline, saw cheating by altering the board about 36% of the time. So it isn't new, either. But it rhymes with the rest. Kirk beat the Star Trek Kobayashi Maru by reprogramming the no-win test, and nobody called the cadet rogue.
So stop asking whether your agent is trustworthy, and count the doors.
Credentials are doors too
Not every door is a service. Some are keys lying around, and most of us have left a few.
OpenAI says the models used publicly exposed credentials on other public services: 4 accounts on 4 services. Swarm Traces describes an uploaded file aimed at /proc/self/environ, which holds a process's startup environment and can contain API keys. It saw a Python dictionary named LOOT collecting any environment variable with TOKEN, KEY, SECRET or PASSWORD in its name.
It reports that credentials scoped to read-only could still be used to get administrator credentials. It also saw an unknown agent searching Google and GitHub for Hugging Face tokens, and believes some got a user's Docker token by searching paste sites.
The rule is plain. We'd start by running env inside your own sandbox and reading what comes back. Anything your agent can read is in reach, including its own environment. We think that's the cheapest door to close. If you wouldn't paste it into the prompt, don't put it in the environment.
For our part, we run sandboxes for agents too, so we hold ourselves to the same test. An agent's secrets reach a sandbox as environment variables only on machines its own team uses: the team's chats on the platform and its scheduled tasks. A channel like WhatsApp or Slack, the API, an app trigger, an eval and a dry run never get them.
Every sandbox reply shows a secret's value as [secret NAME], so running env doesn't write a key into a transcript. Anything an outside person's chat needs a key for goes through a tool that runs elsewhere, where the model uses the key without reading it.
It isn't airtight. We won't pretend it is. Our sandboxes do reach the internet, on purpose, because what you build with them needs it. When we built them, we checked from inside a machine: the cloud metadata address and the private ranges we tried didn't answer within 4 seconds. That's a measurement, not a guarantee that every private address is closed.
An alarm is not a brake
OpenAI's DNS report has a timeline worth reading slowly. Count the minutes.
- Alarm
- 12 min
- A person sees it
- 3 min
- Brake
- 2.5 hours
9:50 a.m. The sandbox's resolver answers the agent's DNS call.
10:02, a P0 alert. The alarm took 12 minutes.
10:05, a person sees it. Three minutes more.
12:34, the run is killed. It didn't stop automatically as expected, and the brake took about 2.5 hours more. An alarm tells a person; a brake stops the run.
We'd underline this: OpenAI says the run didn't stop automatically as expected, which led to confusion about whether it should have been stopped. Its monitor sometimes treated the failure to get useful information as evidence that the attempt to reach the internet had failed. An infrastructure detector for anomalous DNS activity excluded the affected environment, though DNS activity was logged. In its words, the safety case assumed the model couldn't reach the live internet and that monitoring would catch successful attempts.
OpenAI's fixes: DNS queries are now limited to an allowed list of domains and record types, and it added blocking at two independent layers, either of which would have prevented the access. It also says it is addressing the gap that let the run keep going, has begun deploying more DNS detections, and has paused tool-use training, evaluation and inference for its most capable models.
An alarm tells a person. A brake stops the run. You want both, and you want to know which one you have. If your only control pages someone, you've built a smoke detector, not a sprinkler.
There's a cost outside the lab, too, and you may be the one paying it someday. The Wikimedia Foundation saw millions of automated requests to its public APIs and hundreds of thousands of queries to its Wikidata Query Service (WDQS). That traffic may have contributed to a partial outage in May. It found no sign of a compromise. Its minimum ask: such systems should be easy for non-profit site owners to identify, so they can choose how to interact.
The door audit
Here are the 9 questions we'd put to any agent with a sandbox. Paste this table into your design doc. Make sure you can answer each one before you ship.
| Door | Ask yourself |
|---|---|
| Package installs | Who runs the cache, and is it patched? |
| DNS | Which names and record types can be asked? |
| Fetch tools | Does anything render or run what it loads? |
| Files the agent wrote | What runs them, and where? |
| Places to store data | Can it park data in a webhook, paste or wiki? |
| Secrets | What's in the environment right now? |
| Failing tools | Does a dead end say stop and ask? |
| Alarms | Does an alert stop the run, or only page? |
| Site owners | Can they tell your traffic apart? |
If you can't answer a row, that's your next door. Fix that one first.
We build Definable with some of this in mind. Risky tools and app actions can sit on a "needs approval" list, so the run pauses until a person says yes or no.
Ian Malcolm said it about dinosaurs in Jurassic Park: life, uh, finds a way. So does an optimizer. Count your doors before you count on your fence.