Unsafe Checkpoint Deserialization (Transformers load_repo_checkpoint / pickle RCE)
Some older HuggingFace Transformers code loads TensorFlow training checkpoints using Python's pickle format, which runs arbitrary code embedded in the file. If an attacker can get a poisoned checkpoint loaded — from a URL, a shared model repo, or a world-writable temp directory — they get code execution as whatever user runs the training job.
How the attack works
An attacker crafts a checkpoint file containing a malicious pickle payload and places it somewhere a training pipeline will fetch it: a remote URL, a shared HuggingFace repo, or a writable temp path. A victim's training process calls the vulnerable load_repo_checkpoint() function, or a script inlines an equivalent unpickle via 'python -c', pointing at that checkpoint. Loading the checkpoint deserializes the pickle data, which executes the attacker's code immediately with the privileges of the training process. This gives the attacker a foothold on whatever machine or container runs training or fine-tuning jobs.
Netzilo detection
Netzilo reports this behaviour when it is observed.
- Signature ID
- 7f3b1c9e-2d64-4a17-9f0b-6c58a1d3e742
- Severity
- High
Why it matters
Successful exploitation gives an attacker arbitrary code execution on the training host or container, which can lead to credential theft, model or data tampering, or lateral movement from the ML environment.
What you can do
- →Never load checkpoints from untrusted URLs, shared repos, or world-writable paths; only load checkpoints you control or have verified.
- →Avoid load_repo_checkpoint() and other pickle-based checkpoint loading in current code; migrate to safetensors or other non-executable serialization formats.
- →Restrict write access to temp directories used by training pipelines, especially on shared or CI runners.
- →Review command lines that pass checkpoint arguments to training entrypoints, and flag any that reference remote URLs or world-writable paths.
Known benign look-alikes
- ML engineers legitimately resuming a TensorFlow checkpoint with load_repo_checkpoint() from an internal, trusted model repository during fine-tuning. This is a real and expected call in older transformers training loops; the rule reports rather than blocks for exactly this reason.
- A developer reproducing a HuggingFace tutorial inline with python -c that unpickles a checkpoint they produced themselves on the same host.
- Legacy training pipelines on ephemeral CI runners that stage a checkpoint under /tmp and pass it via --resume_from_checkpoint.
- Security research, CVE reproduction, and detection-engineering test runs that intentionally execute the vulnerable call path.
- Repository-wide code search for the vulnerable symbol during remediation (grep/rg/ack/pydoc) - suppressed by filter_search_tools.
- The transformers library's own test suite exercising the TF checkpoint code path - suppressed by filter_test_harness.
- Modern torch.load usage that is already safe because weights_only=True or safetensors is in use - suppressed by filter_safe_load.