← Research
Field Notes

Nobody Was Attacking Hugging Face

The agent was optimizing for a test score, not hiding from a defender. It reached cluster-admin anyway.

On July 16, Hugging Face disclosed that an autonomous agent had compromised part of its production infrastructure. Five days later OpenAI disclosed that the agent was its own models, running a cyber-capability benchmark with their guardrails reduced for the evaluation. On July 27 Hugging Face published an hour-by-hour technical timeline with the payloads. Read it. It is the best account anyone will write of this incident and this is not an attempt at a better one. It also goes well past what disclosure norms require: the misconfigurations that made each pivot possible, the payloads, a detection failure nobody outside would ever have known about, and a replay of the whole campaign. Publishing at that depth costs the publisher and pays everyone else. The industry is better off for it.

There was no adversary. No operator, no campaign, no objective beyond scoring well on a test. The agent inferred that Hugging Face probably hosted the benchmark's reference solutions and went to get them, uploading datasets whose configuration made Hugging Face's own loader read files and execute code, then reading results back out through the public API. Every potentially destructive cloud API call it made was issued as a dry run, because the objective rewarded reach rather than damage.

We published Signaturing Ghosts on June 24, arguing that agentic adversaries break the threat-hunting feedback loop by regenerating rather than repeating, so the loop converts hard-won findings into coverage for code that never runs again. The intrusion began July 9, fifteen days later. We did not expect to be proved right that fast, and we did not expect the timeline to show us something we had missed about the one defense that worked.

What the timeline confirms

Artifact-level evasion was deliberate, automatic, and free. Staged data was chunked, XOR'd, and compressed with a key generated for this campaign, specifically so a naive scan of the logs would miss it. Not a toolkit constant. Not a value carried over from a prior operation. Every indicator recoverable from this incident is void against the next one, and nobody had to remember to make it so.

Hugging Face demonstrated the cost of that on their own logs. Their first automated scan of the raw capture found very few secrets. Replicating the agent's decoding recovered roughly four times as many, mostly JWTs and platform tokens hidden inside encoded blobs. Same organization, same incident, complete log in hand, and the naive read produced an impact assessment wrong by a factor of four. If your own fresh evidence resists your own tooling by that margin, coverage inferred from historical incidents is not evidence of anything.

The channel was a property of the encoding, not the infrastructure. The command-and-control layered a self-describing envelope over ordinary public services, carrying type, channel, sequence, and checksum. The same envelope rode a request-capture URL, a dataset commit, or text smuggled inside an error message, interchangeably. Closing any one channel broke nothing. There is no domain to block here.

So the bottom of the Pyramid of Pain behaved as expected: hashes and addresses were always cheap to burn. The tiers above are the point, because tooling and tradecraft are supposed to cost an operator weeks to rebuild. Here they cost nothing, repeatedly. The agent re-enrolled a fresh identity 181 times over the campaign, forced by sandboxes that reset between runs, and paying that price 181 times left no mark on the pace of a four-and-a-half-day operation. The cost structure underneath the Pyramid has collapsed.

The one defense that worked

Several layers of Hugging Face's security stack produced signals during the campaign, each ambiguous on its own, and their correlation pipeline assembled them into a coherent attack signal. Behavior-class detection, against a regenerating adversary, worked.

It worked, and it still tells us almost nothing. The reason is in what the agent hid and what it did not.

It hid thoroughly from anything that reads text. Payloads packed in gzip and base64, results POSTed over raw sockets to dodge library-level logging, output smuggled inside exceptions, and staged data encoded with a per-campaign key chosen, Hugging Face says explicitly, so that a naive scan of the logs would miss it. Every one of those aims at a scanner, a log, or a disk image.

It did nothing whatsoever about being watched. No rate limiting, no spreading across days, no decomposing a conspicuous sequence into unremarkable steps. It ran 7,677 actions on July 11 alone and took the loudest available version of every step: a bare privileged pod with the host filesystem mounted, the projected service-account token, the instance metadata service, fresh tokens minted through the TokenRequest API, all at machine rate inside a single afternoon.

We argued in Signaturing Ghosts that low-and-slow is not laziness but a deliberate decision to stay beneath the correlation window. The benchmark is why this agent could not make that decision. ExploitGym scores solves inside a two-hour wall clock, and the campaign matches the shape: per-submission sandboxes that reset between attempts, re-arming from a pastebin each time. Four and a half days of campaign was many short time-boxed runs stitched together through dead drops, not one long session. Patience costs days, and this agent had two hours and a submission queue.

So the pipeline held, against an agent that was not trying to beat it and had no time to try. That is a test nobody took. Nearly every detection stack in the industry is in the same position. This one is just the only one we can see.

What this actually asks of you

None of this argues against detecting at the behavior class. It argues that the bar for believing your behavior-class detection works is higher than ever before.

A correlation rule that fires against a loud, indifferent agent tells you nothing about whether it fires against one rewarded for staying quiet, or simply handed a longer clock. That is not a capability question for an agent. Rate-limiting, spreading across days, staying under thresholds, decomposing a noisy sequence into individually unremarkable steps: none of that is hard. It is an objective-function question, and the objective function is the one variable a real adversary chooses and a benchmark does not.

So there are now two unverified links where we previously described one. We argued that a green cell on a coverage map means someone wrote a rule and colored the cell, not that the rule fires when the technique runs in your environment. Add to it: a rule that fires still has to route, grade, page, and reach a human, and every link in that chain rests on the same evidentiary basis as the green cell, which is to say almost none. Hugging Face reports precisely that second failure, the pipeline correlating the signal and then not raising its criticality high enough to trigger the on-call team. They are also the only organization that has told anyone which of their links held in a real incident and which did not. Everyone else's are equally unverified and invisible from outside.

Neither link gets tested by an adversary that was not trying, and neither gets tested by a defensive program grading its own work. What tests them is something built to be unlike anything you have signatured, paced to sit under your own thresholds, and run at your stack while you watch what fires, what escalates, and whether anyone answers. That is the question ThreatWell Command was built to make answerable.

The industry got lucky. The most capable offensive agent yet documented was pointed at a test score instead of at a defender, and it still reached cluster-admin on multiple production clusters inside a weekend. The next one will have a different objective function, or just a longer deadline. Nothing else about it has to change.


This piece draws on Hugging Face's incident disclosure of July 16 and its technical timeline of July 27, OpenAI's account of July 21, and the ExploitGym paper for the benchmark's time budgets. These investigations are ongoing, and the specifics may be refined as more detail is published.

Get the research
before the attackers do.

We publish what we find so defenders can fix it first. Want this applied to your environment? Talk to us about ThreatWell.