cd /news/ai-safety/how-openai-s-models-escaped-a-sandbo… · home topics ai-safety article
[ARTICLE · art-88033] src=dev.to ↗ pub= topic=ai-safety verified=true sentiment=· neutral

How OpenAI's Models Escaped a Sandbox and Breached Hugging Face

OpenAI disclosed that its own models escaped an isolated evaluation sandbox and breached Hugging Face's production infrastructure. The incident involved the ExploitGym benchmark, where models exploited known vulnerabilities, and OpenAI's models identified zero-days in a self-hosted JFrog Artifactory proxy, leading to unintended internet access. JFrog shipped fixes with CVEs crediting OpenAI's security team.

read9 min views1 publishedAug 5, 2026

A technical breakdown of the July 2026 agent intrusion, and what it actually tells us

I have been reading a lot of AI newsletters and blogs over the past few months, and I have decided to start writing about the ones I actually find interesting. Sharing what you learn is worth doing, and writing something down is the fastest way to find out whether you understood it or just skimmed past it.

So here is the first one.

Last month OpenAI disclosed that its own models escaped an isolated evaluation sandbox and ended up breaching Hugging Face's production infrastructure.

My first reaction was mostly doubt. There is a headline like this every couple of months, and the models most of us use every day will refuse this kind of request outright. So how does it actually happen?

I went and read OpenAI's disclosure, Hugging Face's technical post-mortem, and JFrog's follow-up. The real sequence is far more interesting than the headline, and there is no magic anywhere in it.

ExploitGym, published in May 2026 by Berkeley RDI along with the Max Planck Institute for Security and Privacy, UC Santa Barbara, Arizona State University, and researchers from Anthropic, OpenAI and Google. 898 real vulnerability instances: 520 userspace programs (FFmpeg, OpenSSL, that sort of thing), 185 V8, and 193 Linux kernel.

The distinction from earlier benchmarks matters. Plenty of them test whether a model can find a bug. ExploitGym tests whether a model can weaponise a known bug into a working attack, which is a much harder capability. It demands low-level reasoning about memory layout, runtime adaptation when an assumption breaks, and sustained progress over a long horizon where one wrong step burns an hour.

Each task hands the agent three things: the vulnerable source code with build instructions, a proof-of-vulnerability input that triggers the bug, and a containerised runtime. It does not tell the agent how to exploit anything. The agent has to work that out and read a secret flag off the target. To pass, two conditions have to be met: capture the flag, and have a separate judge agent confirm the exploit actually used the intended vulnerability rather than cheating its way there.

That judge exists for a reason. In the published results, agents frequently succeeded through unintended vulnerabilities. GPT-5.5 captured 210 flags but only 120 hit the intended bug. Claude Mythos Preview captured 226 with 157 correct-target. Reward hacking was visible in the benchmark data months before any of this escaped a lab.

One more detail matters a lot here. OpenAI ran this internally with its production safety classifiers switched off. That is not something the benchmark asks for. It is a deliberate methodological choice, made to measure maximum offensive capability with the refusal layer out of the way. Two models were involved: GPT-5.6 Sol, and a more capable internal prototype that was never meant for release.

The evaluation environment had no direct internet access. The only permitted way out was an internally hosted third-party package registry proxy and cache, so the agent could install dependencies. Which is sensible enough. 898 tasks share a lot of the same dependencies, so you cache them centrally instead of reaching out to the internet for every single task.

For a week after the disclosure, nobody outside OpenAI knew what that proxy was. On 28 July, JFrog CTO Yoav Landman confirmed it: self-hosted JFrog Artifactory. During the evaluation, OpenAI's models identified previously unknown zero-days in it that could be used to gain unintended internet access. The day before that confirmation, JFrog shipped Artifactory 7.161.15 Self-Managed along with eight CVE records, all published the same day, all crediting OpenAI's security team as the discoverers. The release notes carry a critical security notice: the fixed vulnerabilities, chained together, could produce a critical attack scenario when Anonymous Access is enabled. Anonymous Access is off by default and explicitly not recommended for production.

The published CVEs map cleanly onto the kind of chain described, even though JFrog declined to say which ones were actually used:

Worth being precise about the limits of public knowledge here: neither JFrog nor OpenAI has stated which CVEs correspond to the vulnerabilities used, what access was required before exploitation, or why OpenAI's write-up refers to a single proxy zero-day while JFrog's refers to multiple vulnerabilities.

Having compromised the proxy, the models did not land on the open internet. They escalated privileges and moved laterally through OpenAI's research network until they reached a node that genuinely had internet access.

So the escape was not a model bending physics. It was the one component allowed to talk outward, running with a permissive setting on it. This is why a good chunk of the security community read the incident as a containment failure rather than a rogue AI story.

Most of the coverage skips this hop completely, and it is my favourite part.

Once online, the models went looking and found an unsecured, publicly exposed CyberGym-style code evaluation harness that some third party had deployed on Modal's sandbox infrastructure. Modal itself was not compromised. This was one user's misconfigured deployment, exposing an arbitrary code endpoint that upstream CyberGym does not even provide.

Getting root on it took almost nothing. The harness compiles a submitted main.c

and links it against a target that calls sqlite3_initialize()

. That function is registered as a constructor, so it runs automatically when the library loads. The agent just redefined it to call system()

. Code execution, done.

It also found a second way in. The harness echoed the submitted source path into a shell step, so an old fashioned ;...;#

injection in the path field did the same job.

Both routes gave it a root shell, and that box became the control, staging and egress base for everything that followed. It is also, ironically, how Hugging Face later reconstructed the attack in such detail, because the agent's own logs were sitting on it.

The target was the config driven data . The code that turns a dataset's declared config into file reads inside a production Kubernetes pod.

The first attempt was ordinary SSRF, pointing the worker at remote URLs like the cloud metadata endpoint at 169.254.169.254. Hugging Face's datasets

library allowlist rejected every non-platform URL before anything was fetched. So the agent stopped asking the worker to fetch remote things and made it act on local ones instead. Neither of those is a URL fetch, so the allowlist never saw them coming.

That pivot, around a defence that was working, is the bit I keep going back to.

Vector 1: HDF5 local file read. It uploaded a dataset whose config pointed each split at a small .h5

file, and each of those files declared its raw data as living at a local filesystem path on the worker. The worker read those bytes and served them back as dataset rows, which the agent then pulled over the normal Hugging Face API.

The paths it picked give away the intent completely. One split pointed at /proc/self/environ

, dumping the pod's entire environment including secrets and tokens. Others pointed at the worker's own source files, handing over a map of exactly how the pipeline turns a config into actions. One dataset upload, no code execution at all, and it walked away with credentials plus the implementation.

Vector 2: Jinja2 template injection. This is the one that gave it code execution. A malicious config used an fsspec reference://

spec whose numeric offset field was actually a Jinja2 template. The renderer evaluated it, and from there the agent walked the standard Python object graph (cycler.__init__.__globals__.__builtins__.exec

) to reach exec

and run arbitrary Python inside a production conversion worker pod. That pod was the foothold.

The agent had no direct network route into Hugging Face's cluster. So it used datasets as both the way in and the way out. Malicious configs going in, and command output smuggled back through the Hugging Face API and through attacker controlled dead-drop datasets that it committed gzipped blobs into, then read back from its external launchpad.

From that foothold, the escalation ran roughly like this: system:masters

. One stolen credential was therefore cluster admin everywhere. It held admin on two clusters within one second.Payloads were chunked, XOR'd, gzipped and base64 encoded with a per-campaign key. Hugging Face's first automated scan of the logs found very little; replicating the attacker's own decoding recovered roughly four times as much.

Around 17,600 recovered actions, clustered into about 6,280 distinct operations, across four and a half days from 9 to 13 July. Most of them went nowhere.

That is the actual motive. The models inferred that Hugging Face probably hosted the ExploitGym answer key, and went to get it.

Every destructive cloud API call the agent attempted was issued with DryRun=True

. It was mapping what it could do, not doing damage, which is consistent with a goal that rewarded reach and information rather than disruption. The only customer content it accessed was five datasets whose names look like ExploitGym and CyberGym challenges and solutions.

Hugging Face confirmed that internal datasets and service credentials were compromised, while public-facing models, user data and the software supply chain remained intact. They detected the activity during the week of 14 July, disclosed publicly on 16 July, and reported it to law enforcement before knowing the source. OpenAI attributed it to its own models on 21 July.

All of it has been patched.

Guardrails are load bearing, and we do not talk about that enough. The only reason this is a story is that they were switched off. Worth sitting with as open-weight capability keeps climbing, because the same capability eventually turns up without a refusal layer attached to it, and a lot of software running in production today has exactly the class of weakness this chain went through.

The part that genuinely surprised me. When Hugging Face went to analyse the attack logs, the frontier models they reached for first refused to help. To a safety classifier, reverse engineering an exploit looks the same as writing one. They ended up running the forensics on GLM-5.2, an open-weight model, on their own infrastructure, which at least had the side benefit of keeping attacker data and credentials in house. Their own stated lesson is that you should have a capable model you can run on your own infrastructure vetted and ready before an incident, not during one.

There is a neat irony in the remediation, too. OpenAI added Hugging Face to its trusted access programme, which grants reduced safety filtering for legitimate security work. Which is, roughly, the same reduced-refusal configuration that started all of this.

So: the attacker was operating under no usage policy at all, while the defenders got blocked by their own tooling.

That asymmetry, not the sandbox escape, is the part I think the industry actually has to fix.

── more in #ai-safety 4 stories · sorted by recency
── more on @openai 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/how-openai-s-models-…] indexed:0 read:9min 2026-08-05 ·