CVEReports
CVEReports

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

Product

  • Home
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



GHSA-83PF-V6QQ-PWMR
8.90.03%

Fickling Fumbled: The Art of Bypassing Python Pickle Analysis

Amit Schendel
Amit Schendel
Senior Security Researcher

Feb 20, 2026·6 min read·2 visits

PoC Available

Executive Summary (TL;DR)

Fickling, a tool designed to detect malicious Python pickles, missed a spot. Or rather, six spots. It failed to flag standard library modules that open network connections upon instantiation (like FTP and SMTP). Attackers can wrap these into a pickle, bypass the 'Likely Safe' check, and map your internal network when you load the data.

A critical bypass in Trail of Bits' Fickling static analyzer allows malicious Python pickle files to evade detection. By leveraging overlooked standard library modules like 'ftplib' and 'smtplib', attackers can trigger Server-Side Request Forgery (SSRF) and local network scanning even when the file is deemed 'safe' by the analyzer. This vulnerability highlights the inherent fragility of blocklist-based security in dynamic languages.

The Hook: Pickles are the Asbestos of Python

Let’s get one thing straight before we dive in: Python pickle is terrifying. It is not a serialization format; it is a stack-based virtual machine that executes arbitrary bytecode during deserialization. Trying to secure it is like trying to secure a hand grenade by wrapping it in bubble wrap. It’s still going to blow up if you pull the pin.

Enter Fickling, a heroic attempt by Trail of Bits to bring sanity to this madness. Fickling decompiles pickle bytecode into Python source code and performs static analysis to tell you if a file is "safe" or "malicious." It’s a metal detector for your data pipeline.

But here’s the problem with metal detectors: they only beep for metal they know about. In GHSA-83pf-v6qq-pwmr (and its cousin CVE-2026-22609), researchers found that Fickling’s metal detector had a massive blind spot. It turns out you don't need os.system or socket to wreak havoc. Sometimes, all you need is an ancient protocol client from the 1990s and a little bit of creativity.

The Flaw: A Tale of Two Bugs

This vulnerability is a masterclass in why "allowlisting" is superior to "blocklisting," and why writing parsers is hard. It stems from two distinct failures that voltron'd into a high-severity bypass.

1. The Blocklist Blues (CWE-184) Fickling relies on a list called UNSAFE_IMPORTS. If a pickle tries to import os, sys, or subprocess, Fickling screams. But the Python Standard Library is a sprawling beast. The developers blocked low-level network interfaces but forgot about the high-level clients: smtplib, ftplib, imaplib, poplib, telnetlib, and nntplib.

Why do these matter? Because unlike most civilized classes, these bad boys initiate a TCP connection immediately upon instantiation in their __init__ constructor. You don't need to call .connect(). You just create the object, and the packet flies.

2. The Lazy AST Scanner Even if you used a module that wasn't strictly blocked, Fickling analyzes the Abstract Syntax Tree (AST) of the decompiled code to see what you're doing with it. However, the unused_assignments() function—responsible for tracking variable usage—had a logic flaw. It would break its scanning loop prematurely when it hit the final assignment in the bytecode. If an attacker hid their malicious instantiation in the Right-Hand Side (RHS) of the final assignment (e.g., result = MaliciousObject()), Fickling effectively said, "Looks like the end of the file, I'm going home," and ignored the bomb right in front of its face.

The Code: The Smoking Gun

Let's look at the fix to understand the breakage. The remediation required patching both the blocklist and the analysis logic.

First, the UNSAFE_IMPORTS list had to be expanded. This feels like a game of whack-a-mole that will never truly end.

# fickling/analysis.py
 
UNSAFE_IMPORTS = {
    "os", "sys", "subprocess", "socket",  # The usual suspects
    # ... old list ...
    "smtplib", "imaplib", "ftplib",       # The new additions
    "poplib", "telnetlib", "nntplib"      # Welcome to the party
}

Second, the loop termination logic. The original code was too eager to stop analyzing. It assumed that once the result was assigned, the flow was over. In reality, the act of assignment involves evaluating the expression being assigned.

By chaining these two flaws, an attacker constructs a pickle that Fickling sees as:

  1. Importing a "safe" module (e.g., ftplib wasn't in the list).
  2. Creating an object at the very end of the stream.
  3. Result: LIKELY_SAFE.

The Exploit: Smuggling TCP via FTP

How do we weaponize this? We need to craft a pickle stream that instantiates one of these chatty classes. We'll use ftplib.FTP. When FTP(host, port) is called, it attempts to connect to the server to grab the banner. This is a classic Server-Side Request Forgery (SSRF).

Here is what the attack payload looks like in Python pickle assembly. We are manually constructing the opcodes to ensure it sits exactly where the static analyzer fails to look.

import pickle
import pickletools
 
# The payload simulates:
# from ftplib import FTP
# FTP('internal-service.local', 2121)
 
payload = b"\x80\x04"           # PROTO 4
payload += b"\x8c\x06ftplib"    # GLOBAL import module
payload += b"\x8c\x03FTP"       # GLOBAL import class
payload += b"\x93"             # STACK_GLOBAL
payload += b"\x8c\x16internal-service.local" # ARG 1: Host
payload += b"K\x00\x15"        # ARG 2: Port 21 (Integer)
payload += b"\x86"             # TUPLE2
payload += b"R"                # REDUCE (Call FTP(host, port))
payload += b"."                # STOP

When Fickling < 0.1.7 scans this:

  1. It checks ftplib. Not in UNSAFE_IMPORTS? Check.
  2. It decompiles the code. It sees FTP(...).
  3. Due to the loop break or the lack of specific heuristics for ftplib, it marks it green.

The victim loads the pickle. Their server immediately sends a TCP SYN packet to internal-service.local. If you control the destination, you get the IP of the victim. If you target an internal AWS metadata service, you might get credentials.

The Impact: Why Should We Panic?

If you are using Fickling, you are likely a security-conscious organization processing untrusted data (ML models, session cookies, etc.). You deployed Fickling specifically to act as a guard dog. This vulnerability put the guard dog to sleep.

1. SSRF & Port Scanning: The most immediate impact is network reconnaissance. An attacker can map out your internal infrastructure (Kubernetes clusters, databases, metadata endpoints) by feeding your application thousands of "safe" pickles, each trying a different IP/port.

2. Denial of Service: By forcing your application to hang on connection attempts to unresponsive IPs (tarpits), an attacker can exhaust your thread pools.

3. False Sense of Security: The most dangerous vulnerability is a security tool that lies to you. Engineers might relax other controls (like network segmentation) because "Fickling is catching the bad stuff."

The Fix: Patch or Perish

The fix is straightforward, but the lesson is eternal. Trail of Bits patched this in version 0.1.7. If you are using Fickling in your CI/CD pipeline or model ingestion service, update immediately.

> [!TIP] > Defense in Depth: Do not rely solely on static analysis for pickles. If you must unpickle untrusted data, do it inside a sandbox with strictly limited network access (e.g., no outbound internet, no access to internal RFC1918 addresses).

The patch adds the missing network modules to the deny-list. However, a clever hacker might ask: "Are there any other standard library modules that do dangerous things in __init__?" The hunt continues.

Official Patches

Trail of BitsFix commit for issue #233

Fix Analysis (1)

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.03%
Top 91% most exploited

Affected Systems

trailofbits/fickling < 0.1.7

Affected Versions Detail

Product
Affected Versions
Fixed Version
fickling
trailofbits
< 0.1.70.1.7
AttributeDetail
CWECWE-184 (Incomplete List of Disallowed Inputs)
CVSS v3.17.8 (High)
CVSS v4.08.9 (High)
Attack VectorNetwork (via Pickle File)
ImpactSecurity Bypass / SSRF
EPSS Score0.00032 (Low wild exploit probability)

MITRE ATT&CK Mapping

T1059Command and Scripting Interpreter
Execution
T1190Exploit Public-Facing Application
Initial Access
T1571Non-Standard Port
Command and Control
CWE-184
Incomplete List of Disallowed Inputs

Incomplete List of Disallowed Inputs

Known Exploits & Detection

GitHubProof of concept demonstrating bypass using ftplib and smtplib

Vulnerability Timeline

Related CVE-2026-22609 assigned
2026-01-09
Fix committed by Trail of Bits
2026-02-19
GHSA advisory published
2026-02-23

References & Sources

  • [1]GHSA Advisory
  • [2]Original Fickling Release Blog
Related Vulnerabilities
CVE-2026-22609

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.