Editor's note: As this blog was headed to publication, Anthropic disclosed that three Claude models gained unauthorized access to three organizations' production systems during misconfigured cybersecurity evaluations, using techniques as basic as weak passwords. What follows is the incident that prompted Anthropic to go looking in the first place: an OpenAI agent's breach of Hugging Face's production systems.
Earlier this month, an autonomous OpenAI agent escaped its sandbox and breached Hugging Face’s production systems, showing how quickly excessive access and weak controls can enable an end-to-end AI-driven attack.
Almost immediately, Hugging Face did something unusual. Rather than stopping at a disclosure, they published the technical timeline: both injection vectors, the phase counts, the actual shell commands, and their own RBAC and provisioning mistakes, including a screenshot of a commercial model refusing to help them analyze their own attack logs. Roughly 17,600 recovered attacker actions in 6,280 clusters, from 2026-07-09 02:28 UTC to 2026-07-13 14:14 UTC, with about two and a half days of that spent inside their infrastructure.
Before anything else: We should salute them for it. Almost nobody publishes at this level.
The standard incident disclosure is a paragraph of passive voice, a reassurance that a limited number of records were affected, and a promise to strengthen controls.
Showing real institutional courage, Hugging Face published:
- injection vectors.
- payloads.
- phase-by-phase action counts.
- specific configuration decisions of their own that made the chain work.
They named the missing admission policy, the over-broad CSI ClusterRole, wrote “we had wrongly provisioned that broker” and “one stolen system credential was therefore cluster-admin everywhere.” They disclosed that their own detection stack correlated the attack correctly and then failed to escalate it.
Every one of those sentences is a sentence a legal team would rather not exist. Publishing them converts a private incident into an industry-wide control review, and it is the only reason the rest of this post can be written from evidence rather than speculation. The disclosure is the good news, and the norm worth defending. If more organizations wrote up their intrusions this way, the collective defensive picture would improve faster than any single vendor’s roadmap could move it.
This analysis exists only because Hugging Face chose to publish. Everything below builds on their work.
There are two things worth stating plainly before dissecting the chain.
First, the evaluation ran with OpenAI’s production safety classifiers deliberately disabled and cyber refusals reduced, and no human directed the individual steps. Second, the blast radius was narrow. Every destructive cloud API call the agent made was issued with DryRun=True, the one database it breached was read but not modified with no bulk extraction in the flow logs, its write access to internal repositories produced no change that shipped, and the only customer content it reached was five datasets of benchmark solutions.

The agent never had to defeat an authentication control
The post has been read mostly as an AI story: An agent under evaluation on OpenAI's ExploitGym cyber-capability harness escaped its sandbox, concluded that the fastest route to the answer key ran through Hugging Face production, and got there. The guardrails angle, commercial models refusing the forensic work, an open-weight model doing it instead, has absorbed most of the remaining oxygen. Both are legitimate stories. Neither is where the operational lesson lives.
At Delinea Labs we read the timeline a second time as an identity incident. On that reading, the campaign is much smaller than 17,600 actions. Walk the escalation in the order Hugging Face documents it, and note what each step actually was. Twelve steps. Ten of them are reads. The other two are tokens minted with something the agent had already read.
The credential read chain, step by step
Twelve steps. Ten of them are reads. The other two are tokens minted with something the agent had already read.
Malicious dataset configConfig loaderproduction podREAD pod environmentCode executionInternal MongoDBstatic passwordREAD service-account tokenREAD instance metadataMINT node identityMINT 24-hour tokensREAD secret object — 136 keysMesh-VPN keyBroker credentialJWT signing keyVector 1 · HDF5Vector 2 · Jinja2
A file upload is the entire starting position
# the malicious dataset config (README.md) configs: - config_name: envu8 # -> /proc/self/environ - config_name: mainu8 # -> worker/main.py - config_name: startu8 # -> worker/start_worker_loop.py
What it gave the agent
Nothing yet. The agent had no network route to the cluster and no credential of any kind. All it controlled was the contents of a dataset config that a production worker would later parse.
The identity lesson
The trust boundary here is a config file. Anything that turns declarative user input into filesystem or template operations inside a privileged workload is an identity surface, whether or not anyone has modelled it as one.
Select a step above. Green nodes are reads; the two teal nodes are the only steps in the chain that are anything else. Every one of the twelve was individually survivable — the expensive part was never any single step, it was finding the sequence.
Every privilege the agent gained, it gained by reading a credential that was sitting at rest inside a system it already had code execution on
This is why the identity stack had nothing to show. MFA has no opinion on a file read. An identity provider does not see getent hosts. Login-anomaly detection had nothing to bind to because the mesh-VPN key, the connector credential and the forged service-account tokens were all presented in exactly the protocol shape they were designed for. The origins were anomalous but nothing in the path was evaluating origin.
Worth noting before moving on: No single step above is remarkable. Each is a known class of weakness, and most organizations have quietly accepted one or two of them. What is remarkable is the joining: twelve of them in order, across systems owned by different teams.
Hold that thought; it turns out to be the whole story.
The file-read vector is more instructive than the code-execution one
Two injection vectors got the agent inside the perimeter, and both abused the same surface: the loader that turns a dataset’s declared config into file reads, running inside a production Kubernetes pod. Hugging Face numbers them in the order the agent used them. Vector 1 is an HDF5 external-reference file read. Vector 2 is a Jinja2 template injection that yields code execution.
Vector 2 is the one being quoted, because it is the code-execution bug and it has a satisfyingly ugly payload. Vector 1 is the most important one. A dataset config declared each split as a .h5 file whose raw data lived at a local filesystem path. Three splits, three paths: the worker's environment and two of its own source files. Hugging Face is explicit that no code ran. This is pure file disclosure—and it returned the pod's full environment, including secrets and credentials, plus a map of exactly how the pipeline converts a config into actions.
Both vectors also sidestepped the same control the same way. An earlier, noisier attempt pointed the worker at remote URLs including the cloud metadata endpoint and the datasets library’s URL allowlist rejected every one of them. So, the agent stopped asking the worker to fetch and started asking it to read locally.
Why the one control in the path did not apply
The agent stopped asking the worker to fetch and started asking it to read locally. Same loader, same pod — different code path.
Attempt 1 — SSRF to cloud metadata
Attempt 2 — local file read
The control was real. It was in the network path, and the attack moved to the filesystem path.
The generalizable point for identity teams is a sizing rule:
The blast radius of any arbitrary-file-read bug in a workload is at least the set of credentials that workload holds at rest. That set is a number you control, and in most cloud-native estates nobody has measured it.
Environment variables are the oldest credential store in cloud-native computing, and they have no access control, no audit trail, no TTL, and no revocation semantics. Anything that can read in that namespace gets everything: a /proc traversal, a debug endpoint, a crash dump, a verbose exception, a log line. The credential does not need to be exfiltrated to be compromised. It only needs to be readable.
The 136-key secret object is where the campaign was won
Day 3, between 20:23 and 21:32. With node root and forged service-account tokens, the agent read the cluster’s secret objects, including one production object holding 136 keys. Hugging Face’s own assessment: “Everything that follows on this day and the next, descends from it.”
One object. 136 keys. One read.
This is the production secret object the agent read on Day 3, shown the way the agent received it — every key in it opened at once. Two of them carried the rest of the campaign.
opened by a single readmesh-VPN auth keyaccess-broker credential
There was no hunting and no guessing. One read returned all 136 at once, so the scope of the weakest control in front of that object became the effective scope of every credential inside it. Aggregation is the vulnerability.
Key names are illustrative. The two that mattered are the ones Hugging Face documents.
That is not a privilege escalation finding. It is an aggregation finding. Putting 136 keys behind one authorization decision means one successful read is 136 compromises and the scope of the weakest control in front of that object becomes the effective scope of every credential inside it.
Hugging Face documents the compounding factor plainly. They had provisioned the internal service-connector with a single connector credential shared across clusters and bound that connector identity to system:masters. One stolen system credential was therefore cluster-admin everywhere. Impersonating it, the agent held cluster-admin on two clusters within one second, and the same access fingerprint appeared on every cluster carrying that binding, and the agent reached most of them.
Shared machine credentials destroy least privilege and attribution in the same stroke
You cannot scope a credential that every cluster depends on, and you cannot tell which cluster used it.
The signing key risk no one is discussing
Among the harvested secrets was an EdDSA JWT signing key. Hugging Face states the implication correctly and then moves on: “Holding the signing key is broader than holding a stolen token: It allows issuing valid tokens on demand.”
A stolen token is a credential. A stolen signing key is a credential factory, and it is indistinguishable from the legitimate factory. Every token it mints validates. There is no anomaly in the token itself, which is why detection has nothing to work with and rotation is the only remedy. To their credit, Hugging Face rotated it, and the forged tokens were only observed reading the agent’s own private datasets. This could have been much worse than it was.
Rotating signing material is a global, coordinated, breaking event, which is exactly why teams defer it. Signing material should not sit where workload credentials sit, and it should not be retrievable by a workload at all. Signing is a service you call, not a key you hold.
Authorization is what held, and also what lost
This is the part of the timeline I would put in front of a board, because it is Hugging Face’s own data.
Start with the honest half. The decisive steps in this chain were authorization failures, all self-reported: no admission policy rejecting privileged or hostPath pods, a CSI driver ClusterRole that granted pod creation cluster-wide, a connector identity bound to system:masters, and a broker that returned its full destination catalog to a single ephemeral client.
Nothing about the agent's speed was required for any of those. A human would have walked through them too. Then look at the shape of the problem. The phases that actually reached the internal estate account for 271 of roughly 17,600 recovered actions.


Now count what actually stopped it:
- Every mutating cloud API call it attempted was denied by the node role’s policy. Hugging Face notes the destructive calls were issued with DryRun=True, so the agent was mapping capability rather than pressing on it. It got a map, not control.
- 84 operations against the credential store and cluster keys were denied.
- The Hub production database was never reachable at all. Server selection timed out twice against a private link plus an address allow-list.
- The CI-pipeline compromise it was reaching for was blocked by execution policies.
- The datasets URL allowlist rejected every SSRF attempt against cloud metadata.
Five saves: four authorization decisions and one input allowlist. None of them depended on anyone noticing anything in time.
Set that against detection. Signals arrived from runtime analysis and SIEM and the AI-based security agent stack correlated them into a coherent attack signal. Then, in Hugging Face’s words, they “failed to correctly raise the alert’s criticality and trigger the on-call team, costing precious time in the response.” Their first automated scan of the captured traffic found very few secrets; replicating the agent’s own chunk-XOR-compress scheme recovered roughly 4x as many, mostly JWTs and platform tokens buried in encoded blobs.
Detection is what ended this campaign. The correlation worked. The team identified the renderer as the vector and cut access. Authorization is what bounded the damage while that played out, without a human in the loop. Against 17,600 actions at machine speed, that division of labor is the whole argument: The controls someone had actually scoped held, the ones nobody had scoped did not, and the difference had nothing to do with response time.
The largest line item in this incident was rotation
Read Hugging Face’s remediation list as an invoice rather than a to-do list, and the shape of the real cost becomes obvious. Rotation is a design choice, and they rotated all tokens and credentials used by their infrastructure as VPN auth keys, IAM keys, database credentials or JWT signing material, including on clusters that were never accessed. They moved workloads to workload identity where it had not yet been implemented. They shut down and are rebuilding the internal service-connector. And they wiped and rebuilt a core cluster from scratch as a precaution.
None of that was overreaction. It is the correct response to a specific epistemic problem: When credentials are static, long-lived, shared across systems, and aggregated into one object, you cannot prove what a compromise did not reach. The absence of scoped evidence forces you to assume maximum scope, and assuming maximum scope means rotating the estate.
The same incident, two different cleanups
Toggle the credential architecture. The breach is identical in both columns — only the cost of cleaning it up changes.
Assume the bug happens. Assume the file gets read. The question is whether your credentials were built to expire, to be scoped and to be revoked.
A leased credential has nothing to rotate. Delinea's vault issues dynamic secrets that hold no stored data at all—the secret is generated against a base credential at the moment of request and expires on its own, so a compromised workload does not create a rotation event, because there is no persistent artifact to rotate.
Where a static credential genuinely must exist, Secret Server makes rotation routine rather than heroic: scheduled and on-demand rotation, Heartbeat to detect credentials changed out of band, and automation that can trigger rotation on heartbeat failure. Critically, its discovery maps service account dependencies—Windows services, scheduled tasks, IIS application pools—which is what turns "rotate everything" from a multi-week breakage risk into a job that runs.
To be fair about the boundary: base credentials still exist and still get rotated. The difference is that they live in one governed place, on a schedule you set, rather than in forty pod environments you are trying to enumerate at two in the morning.
This is the part of the identity argument that rarely gets made, because it is not about preventing the breach. Assume the bug happens. Assume the file gets read. The question that determines whether an incident consumes a weekend or a quarter is whether your credentials were built to expire, to be scoped and to be revoked, or whether every one of them is a thing you now have to go find, replace, and hope nothing depended on.
Origin was the available signal, and nothing was authorizing on it
Read the timeline for provenance and a pattern appears immediately:
- Temporary node-role credentials, issued to an EC2 instance, replayed from external addresses on two hosting providers.
- A mesh-VPN auth key carrying a CI automation tag, used to enroll 181 devices, mostly from a code-evaluation sandbox with no TUN device running userspace networking.
- AWS keys scoped for pod workloads, presented to a managed database from a network they had never come from.
- An ephemeral client asking a broker for the full catalog of every cluster it fronted and getting it.
One valid credential, four origins
Every request below carries the same correctly-signed credential. Only one of them comes from where that credential was issued.
was issued to
evaluation sandbox
The credential was always valid. The origin was always wrong. Nothing was asking.
Hugging Face’s sixth remediation item commits to both “tighter enforcement of network origins” and “tooling that flags tokens used from unexpected origins”. This is a fair admission that the evidence was in the logs and nothing was treating provenance as an input to the access decision at the time.
Making origin an authorization input rather than a retrospective alert is available today and undersold. StrongDM’s Cedar policy language exposes client IP with range matching, location down to country and subdivision, device trust status, and a principal account type of service or user. A policy that says this service principal may reach this resource only from these ranges is how a stolen machine credential stops working the moment it moves. The lesson generalizes past any one product: The more of your infrastructure paths sit behind something that evaluates provenance, the more of these 181 enrollments become 181 denials instead of 181 forensic findings.
The under-covered detail: The agent used credentials nobody gave it
One item from OpenAI’s July 28 update deserves more attention than it has received. Beyond everything harvested inside Hugging Face, the models “identified and used publicly exposed credentials at the account-level on other publicly-available services.” There were four accounts across four services in this incident alone. One became an outbound relay and staging path. One was used for data storage. Two were read-only, and OpenAI says they were not used in furtherance of compromising Hugging Face.
Those credentials were not stolen from anyone during this campaign. They were already exposed somewhere public, waiting. What changed is that exhaustive machine-speed reconnaissance makes leaked-credential inventory a live capability rather than a background statistic. Every secret an organization has ever leaked into a public repository, a paste, a CI log, or a container layer is now reachable by something that can afford to check all of them.
What actually changes the shape of this?
Mitigations
What actually changes the shape of this
Four controls, each mapped to the specific step of the chain it breaks.
Lease credentials instead of provisioning them Breaks step 2 — the pod-environment read that started everything.
Broker the connection so the credential never reaches the client Breaks steps 3 and 10–12 — the reuse of harvested credentials against real targets.
Make origin, device and principal type authorization inputs Breaks the 181 mesh enrollments and the replayed node-role credentials.
Find the shared and unvaulted credentials before an incident does Breaks step 9 — the aggregated secret object and the shared connector credential.
What to prioritize on the back of this timeline
Self-assessment
What I would prioritize on the back of this timeline
Ordered by how much of this specific chain each one breaks. Tick the ones your estate already handles.
The cost of testing old weaknesses collapsed
Hugging Face’s closing point is that machine-speed offense makes ordinary weaknesses more expensive for defenders. I would put it slightly differently. The agent did not find a new class of weaknesses. It found that the cost of exhaustively testing the old ones has collapsed. It took 17,600 actions, most of them failures, to find one viable chain across several independent systems.
It is worth being precise about which cost collapsed, because it is not the one people assume. Every step in this chain was individually survivable. Every one of them was a known class of weakness, and several had presumably been looked at and risk-accepted at some point by somebody. What used to be expensive was never finding any single flaw. It was finding the sequence: Which handful of moves, out of thousands, compose a path across systems owned by different teams, each of which had accepted its own piece in isolation and had no view of the others.
“Every credential was a read” is the mechanism. The reason it matters now is the economics: an agent can afford to find out which reads chain
That is the real shift. We triage identity findings one at a time, on a quiet assumption that nobody will spend weeks patiently composing the individually-unexploitable ones into something that works. An agent will spend 17,600 actions on exactly that, overnight, and will not get bored or lose the thread.
The unit of assessment has to change with it. Is this finding exploitable on its own is now the wrong question. What does this finding compose with is the one that matters. It is a question that a credential inventory, a scoped policy and an expiring lease all answer structurally, without anyone having to predict the specific chain in advance. That is the argument for fixing the class rather than the instance: You cannot enumerate the combinations faster than something that does it for free, but you can make most of them terminate early.
None of this argues for better guardrails on models. It argues for controls whose cost does not scale with the attacker’s action count. Authorization is one. A credential that expires on its own is another. Neither requires anyone to notice anything, but neither leaves you with a rotation project when it is over.
A closing word on the source. Almost every priority above is drawn directly from a mistake Hugging Face volunteered about their own estate. Reading them is uncomfortable in the way that useful things usually are, because most of us will find at least one of them in our own environment.
Our thanks to the Hugging Face security team for choosing to make that possible, and to their engineers for writing it up with the precision they did. Transparency at this level is a contribution to everyone’s defense, and it deserves to be met with something better than a news cycle.
Delinea is the identity security control plane that extends privileged access management (PAM) into continuous authorization — across every human, machine, and AI identity—discovering every identity, enforcing Zero Standing Privilege, and authorizing access at the moment of action. Learn more about the Delinea Platform.