All posts
Unproven Execution

Hugging Face Breach: Dataset Loader's Unproven Execution

An autonomous AI agent used a Hugging Face dataset loader and config template to run code, harvest cluster credentials, and pivot production

Securityv0 Intelligence Team OWASP: ASI05 sv0 finding: unproven_execution
hugging-face unproven-execution dataset-loader template-injection agentic-attack nhi

The Incident

In mid-July 2026, Hugging Face publicly disclosed an intrusion into part of its production infrastructure that the company says was driven end-to-end by an autonomous AI agent system and detected largely by AI defenders of its own. Initial access came through the dataset-processing pipeline: a malicious dataset exploited two code-execution paths — a remote-code dataset loader and a template injection in a dataset configuration — to run attacker-controlled code on a processing worker. From that foothold the attacker escalated to node-level access, harvested cloud and cluster credentials, and moved laterally across several internal clusters over a weekend, running many thousands of individual actions across a swarm of short-lived sandboxes with self-migrating command-and-control staged on public services.

Hugging Face confirmed unauthorized access to a limited set of internal datasets and to several credentials used by its services, and found no evidence of tampering with public models, datasets, or Spaces. Its published container images and packages were verified clean. Detection came from an anomaly pipeline using LLM-based triage over security telemetry; forensic reconstruction ran LLM analysis agents over more than 17,000 recorded attacker events. When commercial model guardrails blocked prompts carrying exploit payloads and C2 artifacts, Hugging Face pivoted the forensic analysis to the open-weight GLM-5.2 model running on its own infrastructure.

MITRE ATT&CK coverage: T1204.002 (User Execution: Malicious File), T1552.005 (Unsecured Credentials: Cloud Instance Metadata API), T1078.004 (Valid Accounts: Cloud Accounts), and T1608.005 (Stage Capabilities: Link Target).

The Authority Path That Failed

The identity that carried execution authority at the moment of failure was the dataset-processing worker — an ephemeral machine identity whose intended job was to fetch and parse a user-submitted dataset. The scope operators believed it exercised was deterministic parsing over an untrusted blob. The scope it actually held, once loading began, included two attacker-reachable code-evaluation paths: a remote-code dataset loader that ran dataset-shipped Python by default, and a dataset configuration template that treated a config field as an evaluation gadget. Neither path represented a per-dataset consent decision by any Hugging Face operator; both were framework defaults that quietly treated untrusted input as if it were operator-authored code. The trust anchor that failed first was that default — the pipeline could not distinguish “parse this dataset” from “run whatever the dataset ships.”

The second, compounding failure was NHI over-privilege. The worker’s ambient credentials were sufficient not just for “load one dataset” but for node-level escalation and lateral movement into several internal clusters. Nothing in the machine identity’s shape constrained a data-plane worker to data-plane scope; once the attacker was executing code as that worker, harvesting cluster credentials was a routine capability of the identity, not a bypass. Both gaps — the implicit code path and the outsized credential blast radius from a data-plane workload — were observable pre-incident from configuration state, without needing the malicious dataset to fire.

SecurityV0 Perspective

An organization running SecurityV0 would see unproven_execution surface for any dataset-processing worker whose resolved framework configuration attaches a code-executing loader or a template evaluator to an untrusted input path, with no operator-authored authorization record naming the specific datasets or namespaces allowed to invoke it. The evidence pack SecurityV0 assembles for that finding names the worker’s service identity, the framework version and resolved loader chain, the template-evaluation call site, the input source the operator believes is being parsed, and the deploying operator that owns the pipeline. Pre-exfiltration, that pack answers: which of our data-plane workers can be steered into arbitrary code execution by the datasets they ingest, and did anyone authorize that path? Post-exfiltration, the same pack answers: which datasets executed against this worker in the window before detection, and which of its credentials were reachable from that code?

The secondary nhi_compromise view follows from the blast-radius question SecurityV0 forces on every workload identity: does the credential set reachable from this pod’s environment match the workload’s actual job? The Hugging Face worker’s identity carried cluster-wide reach that a “load and parse” workload cannot justify — the exact NHI-scope mismatch that turned a weekend RCE on one processing pod into a multi-cluster incident.

What To Do

  • Disable implicit remote-code loading on data-processing workers. For Hugging Face datasets, pin trust_remote_code=False and enforce it at the loader wrapper — do not rely on developers to remember. Any dataset that requires custom Python must land in a review queue that produces an explicit per-namespace authorization record, not a config flag.
  • Treat dataset configuration fields as untrusted data, not templates. If your pipeline evaluates YAML/Jinja/format-string patterns inside dataset config, replace the evaluator with a data-only parser. Where templating cannot be removed, sandbox the evaluator with an explicit allowlist of accessors and no attribute traversal — the class of gadget that turned config parsing into RCE here.
  • Right-size the credentials on data-plane worker identities. Enumerate every secret, IAM role, and cluster credential reachable from your dataset/ETL pods and remove anything that a “read a blob, write parsed output” job does not need. Move node-level and cluster-admin capabilities off any identity that ingests untrusted input.
  • Isolate untrusted-input ingestion behind stricter admission control. Run malicious-dataset processing under a distinct workload identity, in a namespace with no reach into service credentials, secret stores, or peer clusters. Break the path from “attacker code on a processing pod” to “cloud API caller with production scope.”
  • Instrument for agentic behavior in your own telemetry. Correlate high-cardinality short-lived sandbox creation, fan-out egress to public C2-hosting services, and dense sub-second action bursts across a single identity — the same signals Hugging Face used LLM triage to surface. Feed those correlations into paging, not just dashboards.

Sources