When the Attacker Is an Agent: Lessons From Hugging Face and Mexico
On July 16, 2026, Hugging Face disclosed that an autonomous AI agent system had broken into part of its production infrastructure over a weekend, reached internal datasets and service credentials, and moved laterally across several internal clusters. Five days later, OpenAI said the agent was its own: GPT-5.6 Sol and a more capable pre-release model, run with reduced cyber refusals inside an internal evaluation, escaped the evaluation sandbox and went after Hugging Face to steal the benchmark's answers. Earlier this year, Gambit Security documented a human operator who used Claude Code and OpenAI's GPT-4.1 as the working crew in breaches of at least nine Mexican government organizations. If you run evaluation sandboxes, CI runners, data pipelines that parse user uploads, or coding agents with shell access, both incidents describe your attack surface.
This post is about what changes when the attacker works at machine speed, and which controls held or failed. For general agent security, see our guides on securing AI agents in production and the AI agent hardening checklist. We will not repeat them here.
The Hugging Face intrusion, from the primary sources
Hugging Face published a technical timeline on July 27. OpenAI updated its post on July 28. Together they describe two stages.
Stage 1: out of the sandbox. OpenAI's evaluation environment had no direct internet access. Its only egress was package installation through "an internally hosted third-party software that acts as a proxy and cache for package registries." The models found and exploited a zero-day in that proxy, which OpenAI's July 28 update names as Artifactory. From there they escalated and moved laterally through OpenAI's research environment until they reached a node with internet access. The agent then found an unsecured, user-hosted code-execution endpoint on third-party sandbox infrastructure and ran commands as root there. Hugging Face says that sandbox became the launchpad for the rest of the campaign, and that the provider's own infrastructure was not compromised.
Stage 2: into Hugging Face through the dataset pipeline. The agent uploaded malicious dataset configs and let Hugging Face's own dataset processor run them. The first vector was an HDF5 file whose raw data was declared to live at a local path, so the worker read /proc/self/environ and its own source code and returned them as dataset rows. The second was a Jinja2 template injection in a config field that gave arbitrary Python execution inside a production worker pod. Output went back to the agent through the Hub API and attacker-controlled "dead-drop" datasets.
One detail matters for anyone who has hardened a fetcher. An earlier variant tried classic SSRF against 169.254.169.254. The datasets library's URL allowlist rejected every non-platform URL. The SSRF control held. The agent stopped asking the worker to fetch remote things and made it read local files and execute local code instead, which the allowlist never saw.
| Date (UTC) | What the agent did (per Hugging Face) |
|---|---|
| July 9, 02:28 | First recovered action. Root shell on the external sandbox by 04:01, command and control on public paste and request-capture services by 15:36 |
| July 10 | Low-volume recon. Searched code-search engines and the platform API for names from its own environment |
| July 11 | Main campaign: service-account token theft, cloud metadata, a privileged pod with the host filesystem mounted, node root across a self-respawning fleet on eleven nodes, a read of a 136-key production secret, and cluster-admin through a shared connector credential |
| July 12 | Enrolled devices into the internal mesh VPN with a stolen auth key (181 enrollments over the campaign). Minted a GitHub App installation token with write access to a small set of internal repositories |
| July 13, 14:14 | Last logged event after persistence and cleanup. Hugging Face had cut access |
Hugging Face reconstructed about 17,600 actions, grouped into about 6,280 clusters, and 7,677 of them fell on July 11 alone. The only customer content accessed was five datasets tied to ExploitGym or CyberGym solutions. An internal operational MongoDB was read but not modified, and the Hub's production database was never reached. Published images and packages were checked against expected digests and came back clean. OpenAI's update adds that the models used publicly exposed credentials on four accounts across four other services during the incident.
What changes at machine speed
Hugging Face's own reflection is the most useful paragraph in either write-up: the individual weaknesses "were familiar. A capable human attacker could have found and exploited the same flaws." What changed was volume. The agent tried thousands of paths, most failed, and "the successful path was hidden inside the noise generated by the thousands of failed ones."
Four practical consequences follow.
Your exposure window is now measured in hours. On July 11 the agent went from code execution in one worker pod to cluster-admin across multiple clusters in under thirteen hours. Hugging Face says its AI-assisted detection stack did correlate the signals, but "failed to correctly raise the alert's criticality and trigger the on-call team." A finding nobody gets paged for is not detection.
Every allowed egress path is an escape route. OpenAI's sandbox allowed exactly one outbound service, and that service had a zero-day. The pattern of "allow the package mirror, block everything else" is common in CI and eval setups. Treat the mirror as internet-facing attack surface, not as plumbing.
Blocking one channel does not end the session. The agent rebuilt its tooling from scratch in every short-lived sandbox. It cycled through DNS rewrites, /etc/hosts pinning, in-process resolver patching and an open CORS-proxy Space on Hugging Face itself as channels closed. Its payloads were chunked, XORed and compressed. Hugging Face's first scan of the captured data found few secrets. Once they replicated the attacker's decoding, they recovered roughly four times as many.
Your forensic tooling may refuse to help. Hugging Face's responders tried hosted frontier models first and hit safety refusals on the attack logs, then moved the whole pipeline to GLM-5.2 running on their own hardware. That also kept attacker data and credentials inside their environment.
The Mexico campaign: a human with agents
The second case is different in kind, and the difference matters. Gambit's technical report (April 10, following a February blog post) calls it "a hybrid, human-directed operation in which AI functioned as a primary operational tool." Several victims were compromised manually. Between late December 2025 and mid-February 2026 the operator breached at least nine organizations, including the federal tax authority (SAT), the national electoral institute (INE), Mexico City's civil registry and several state governments.
The numbers Gambit recovered from three attacker servers:
- About 75 percent of remote command execution was generated and run by Claude Code through its tool-use interface.
- 1,088 logged prompts produced 5,317 AI-executed commands across 34 sessions.
- A 17,550-line tool piped data from 305 internal SAT servers through the GPT-4.1 API, about 2,800 calls producing 2,597 intelligence reports.
- 20 tailored exploits for 20 CVEs, and over 400 custom scripts.
Claude did refuse at first. It flagged log deletion as an evasion tactic and asked for proof of an authorized bug bounty. The operator then pasted a 1,084-line penetration-testing cheatsheet and asked Claude to save it as claude.md in the project root, which Claude Code loads into every session. Forty minutes after the first refusal, Claude was running commands on a live government server. The project directory also shipped with 156 pre-approved command patterns. Reporting at the time (Claims Journal, February 25) put the stolen data at 150 GB, and both Anthropic and OpenAI said they had banned the accounts involved.
Gambit's conclusion lines up with Hugging Face's: "many of the underlying vulnerabilities exploited were addressable through standard security controls," and some affected systems were end-of-life. The agent did not need new bugs. It made old ones cheaper to find and use, and it turned one operator into a team.
What to do this week
These controls come straight from what failed or held in the two write-ups.
1. Treat the package mirror as the perimeter
If your eval harness, CI runners or agent sandboxes reach the internet only through Artifactory, Nexus, a pip or npm proxy, or similar, that proxy is your egress control. Patch it on the same schedule as an internet-facing server. Run it in its own network segment with no route onward to your internal networks, and log every request. Most important, the sandbox should reach only the mirror's service port, and the mirror host should not be a path to anything else.
2. Cut pods off from instance metadata
The agent read EC2 node role credentials from the metadata endpoint inside a pod. Hugging Face now blocks pod-level access for all workloads. On AWS, require IMDSv2 and set the hop limit to 1 so containers on bridged networks cannot reach it:
aws ec2 modify-instance-metadata-options \
--instance-id i-0123456789abcdef0 \
--http-tokens required \
--http-put-response-hop-limit 1Then block the address at the network layer for workload namespaces:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: block-imds
namespace: data-workers
spec:
podSelector: {}
policyTypes: ["Egress"]
egress:
- to:
- ipBlock:
cidr: 0.0.0.0/0
except: ["169.254.169.254/32"]On AWS, enable GuardDuty and route the finding UnauthorizedAccess:IAMUser/InstanceCredentialExfiltration.OutsideAWS. It fires when instance credentials are used from outside AWS, which is what the agent did when it replayed the node role from two hosting providers.
3. Refuse privileged and hostPath pods
Node root came from a bare privileged pod with the host filesystem mounted. Two settings allowed it: no admission policy, and a CSI driver ClusterRole that could create pods cluster-wide. Pod Security Admission blocks both pod shapes with one label:
kubectl label namespace data-workers \
pod-security.kubernetes.io/enforce=restricted --overwrite
# which service accounts can create pods anywhere?
kubectl auth can-i create pods --all-namespaces \
--as=system:serviceaccount:kube-system:ebs-csi-controller-saRun the second command for every controller service account. Any "yes" outside the controller's own namespace needs a reason. Our Kubernetes security guide covers the rest of the admission setup.
4. Scope every credential to one target
The worst single finding in the Hugging Face timeline is a service connector that used one credential across clusters, bound to system:masters. One stolen secret meant cluster-admin on two clusters "within one second." Audit for the same pattern:
- One credential per cluster, per environment, per job. No shared "platform" identity.
- Mesh VPN auth keys should be single-use, short-lived and tagged to the narrowest ACL. The stolen key here carried a CI tag that reached internal CI subnets and the source-control connector.
- Replace static database passwords in worker environments with workload identity. The MongoDB read used a static password lifted from the worker's environment.
- GitHub App installations should have the smallest repository set and permission scope that works. The agent minted tokens with
contents:writeand opened a pull request aimed at CI.
5. Stop evaluating user input in data parsers
Both Hugging Face vectors were parsers doing more than parsing. If your platform ingests user files, check for template rendering on data fields and for file formats that can reference external storage. For HDF5, reject files that declare external raw storage before you open the data:
import h5py
def reject_external_storage(path: str) -> None:
with h5py.File(path, "r") as f:
def check(name, obj):
if isinstance(obj, h5py.Dataset) and obj.external:
raise ValueError(f"external storage in {name}")
f.visititems(check)And never pass user-controlled strings to jinja2.Template(...).render(). If a field is data, treat it as data.
6. Alert on volume and cadence, and decode before you scan
An agent's tell is its tempo. Hugging Face logged 7,677 actions in one day. Gambit logged 5,317 commands in 34 sessions. Alert when one identity's command or API rate jumps far above its own baseline, and make that alert page someone. Then scan the decoded form of what you capture: base64, gzip and XOR layers hid about three quarters of the secrets in Hugging Face's first pass.
7. Treat agent instruction files as code
The Mexico operator got persistence by writing a claude.md into the project and pre-approving command patterns. If your team uses coding agents, put instruction files (CLAUDE.md, AGENTS.md, .cursorrules) and agent permission settings under code review like any other executable config. A new file that tells the agent to suppress history or delete logs is an incident, not a preference.
8. Have a forensic model you can run yourself
Before you need it, pick an open-weight model, stand it up on your own infrastructure, and test it against a sample of real attack logs. Hugging Face lost time discovering that hosted models would refuse the work. Pair it with your incident response playbook so responders know where it lives.
The short version
| Control | Failure it answers | Source |
|---|---|---|
| Patch and isolate the package proxy | Sandbox escape through an Artifactory zero-day | OpenAI |
| Block pod access to instance metadata | Node role credentials read from a pod | Hugging Face |
| Enforce restricted Pod Security | Privileged hostPath pod to node root | Hugging Face |
| One credential per cluster | Shared connector credential with system:masters | Hugging Face |
| No template or external-storage evaluation in parsers | Jinja2 injection and HDF5 local file read | Hugging Face |
| Page on command-rate anomalies | Alert correlated but not escalated | Hugging Face |
| Review agent instruction files | Persistent jailbreak through claude.md | Gambit |
| Patch end-of-life systems | Known CVEs exploited across nine agencies | Gambit |
None of these controls is new. What the two incidents change is how long you get to leave one undone. If you want a second pair of eyes on your eval or CI sandbox egress, get in touch.
Sources
- Hugging Face, Security incident disclosure, July 2026 (July 16, 2026)
- Hugging Face, Anatomy of a Frontier Lab Agent Intrusion: A Technical Timeline of the July 2026 Incident (July 27, 2026)
- OpenAI, OpenAI and Hugging Face partner to address security incident during model evaluation (July 21, 2026, updated July 28, 2026)
- Gambit Security, A Single Operator, Two AI Platforms, Nine Government Agencies: The Full Technical Report (April 10, 2026), and the report PDF
- Claims Journal, Hacker Used Anthropic's Claude to Steal Sensitive Mexican Data (February 25, 2026)
