safetensors Validation Bypass PoC β Security Research
Responsible disclosure to huntr.com. Do not use for malicious purposes.
Vulnerability Summary
safe_open(framework="pt") in safetensors silently returns a torch.Tensor whose
bytes were never validated, despite completing without error.
The HuggingFace Hub cache stores model files as symlinks by design:
~/.cache/huggingface/hub/models--org--model/
blobs/
a3f2c1d9... β actual bytes (named by hash)
snapshots/
main/
model.safetensors β ../../blobs/a3f2c1d9... β SYMLINK
safe_open() makes two independent open(2) syscalls on this symlink path:
- Rust
File::open(path)β validates header β inode A β torch.UntypedStorage.from_file(path)β re-opens path β may get inode B β
Any user-level code (malicious pip package, notebook cell) with write access to
~/.cache/huggingface/hub/ can atomically swap the symlink between these two
opens β bypassing all safetensors validation silently.
Files
| File | Purpose |
|---|---|
poc_hf_realworld.py |
Primary PoC β exact HF Hub cache layout, two separate processes, no torch required |
poc_adversarial_impact.py |
Impact PoC β 3 scenarios: moderation bypass, NaN DoS, APT backdoor |
poc_toctou.py |
Secondary PoC β racy/deterministic/simulation variants |
good_model.safetensors |
Legitimate model (4Γ4 float32, 0x00 fill) |
malicious_model.safetensors |
Malicious model (same structure, 0xDE fill) |
Reproduce (no torch needed)
pip install safetensors numpy
# Primary: HF Hub cache attack (two processes)
python3 poc_hf_realworld.py
# Impact: see actual model behavior change
python3 poc_adversarial_impact.py
Confirmed Output
[STAGE 1] HuggingFace Hub cache structure created:
models--google--flan-t5-base/
βββ blobs/
β βββ a3f2c1d9ee4b72f1... β GOOD model (0x00 bytes)
β βββ deadbeef12345678... β EVIL model (0xDE bytes, attacker pre-staged)
βββ snapshots/main/
βββ model.safetensors β ../../blobs/a3f2c1d9... β SYMLINK
[VICTIM] FDβ open β inode 12791353
[VICTIM] Validation β PASSED β (tensors: ['weight'])
[ATTACKER] Swapping symlink NOW (atomic rename)...
[ATTACKER] Swap complete!
[VICTIM] FDβ open β inode 12791354
β RACE WINDOW CONFIRMED
inode validated = 12791353 (what safetensors checked)
inode torch maps = 12791354 (what the tensor is backed by)
All 64 bytes of tensor data are attacker-controlled
Affected Component
- File:
bindings/python/src/lib.rs - Function:
Open::new()lines 554β698 - Frameworks affected: PyTorch β₯ 1.11.0, Paddle β₯ 3.1.1
- NumPy backend: NOT affected
Inference Providers NEW
This model isn't deployed by any Inference Provider. π Ask for provider support