Your RSA-2048 keys break in 2030. Find every one of them before attackers do.
🐍 PyPI
Not in CISA KEV

GHSA-wccx-j62j-r448 — fickling

Fix: trailofbits/fickling@8c24c6e

GHSA-wccx-j62j-r448 is a security vulnerability in fickling. A fix is available for fickling — see the affected versions and patch details below.

Fickling has `always_check_safety()` bypass: pickle.loads and _pickle.loads remain unhooked

Published
Mar 4, 2026
Updated
Mar 4, 2026
Affected
1 pkg
Patched
1 / 1
Exploits
None indexed
Exploitation data as of Mar 4, 2026 · OSV.dev, FIRST.org (EPSS)

Real-World Exposure

1 pkg affected
🐍fickling

Real-time download stats are indexed for npm and PyPI packages. This vulnerability affects PyPI packages — download data is not available via public APIs for these ecosystems.

Description

Assessment

The missing pickle entrypoints pickle.loads, _pickle.loads, and _pickle.load were added to the hook https://github.com/trailofbits/fickling/commit/8c24c6edabceab156cfd41f4d70b650e1cdad1f7.

Original report

Summary

fickling.always_check_safety() does not hook all pickle entry points. pickle.loads, _pickle.loads, and _pickle.load remain unprotected, enabling malicious payload execution despite global safety mode being enabled.

Affected versions

<= 0.1.8 (verified on current upstream HEAD as of 2026-03-03)

Non-duplication check against published Fickling GHSAs

No published advisory covers hook-coverage bypass in run_hook(). Existing advisories are blocklist/detection bypasses (runpy, pty, cProfile, marshal/types, builtins, network constructors, OBJ visibility, etc.), not runtime hook coverage parity.

Root cause

run_hook() patches only:

  • pickle.load
  • pickle.Unpickler
  • _pickle.Unpickler

It does not patch:

  • pickle.loads
  • _pickle.load
  • _pickle.loads

Reproduction (clean upstream)

import io, pickle, _pickle
from unittest.mock import patch
import fickling
from fickling.exception import UnsafeFileError

class Payload:
    def __reduce__(self):
        import subprocess
        return (subprocess.Popen, (['echo','BYPASS'],))

data = pickle.dumps(Payload())
fickling.always_check_safety()

# Bypass path
with patch('subprocess.Popen') as popen_mock:
    pickle.loads(data)
    print('bypass sink called?', popen_mock.called)  # True

# Control path is blocked
with patch('subprocess.Popen') as popen_mock:
    try:
        pickle.load(io.BytesIO(data))
    except UnsafeFileError:
        pass
    print('blocked sink called?', popen_mock.called)  # False

Observed on vulnerable code:

  • pickle.loads executes payload
  • pickle.load is blocked

Minimal patch diff

--- a/fickling/hook.py
+++ b/fickling/hook.py
@@
 def run_hook():
-    pickle.load = loader.load
+    pickle.load = loader.load
+    _pickle.load = loader.load
+    pickle.loads = loader.loads
+    _pickle.loads = loader.loads

Validation after patch

  • pickle.loads, _pickle.loads, and _pickle.load all raise UnsafeFileError
  • sink not called in any path

Regression tests added locally:

  • test_run_hook_blocks_pickle_loads
  • test_run_hook_blocks__pickle_load_and_loads in test/test_security_regressions_20260303.py

Impact

High-confidence runtime protection bypass for applications that trust always_check_safety() as global guard.

Affected Packages

1 total 1 fixed
EcosystemPackageVulnerable rangeFix
🐍PyPIficklingall versions0.1.9pip install --upgrade 'fickling==0.1.9'

Detection & mitigation playbook

Open-source dependency
  1. Detect

    Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for fickling, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.

  2. Fix

    Update fickling to 0.1.9 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms GHSA-wccx-j62j-r448 is resolved across your whole dependency graph.

  3. Workarounds

    If you can't upgrade right away: gate or disable the affected feature, validate untrusted input at the boundary, and avoid passing attacker-controlled data into the vulnerable path. O3's runtime protection blocks exploitation in production as an interim safeguard until the upgrade lands.

  4. How O3 protects you

    O3 Security's impact-aware SCA analyses which vulnerable code paths your application actually calls, so a match like GHSA-wccx-j62j-r448 can be triaged on real exposure rather than presence alone.

Tailored to GHSA-wccx-j62j-r448. Runtime protection reduces exposure until a permanent patch is applied and verified — it complements patching, it doesn't replace it.

Frequently Asked Questions

# Assessment The missing pickle entrypoints `pickle.loads`, `_pickle.loads`, and `_pickle.load` were added to the hook https://github.com/trailofbits/fickling/commit/8c24c6edabceab156cfd41f4d70b650e1cdad1f7. # Original report ## Summary `fickling.always_check_safety()` does not hook all pickle entry points. `pickle.loads`, `_pickle.loads`, and `_pickle.load` remain unprotected, enabling malicious payload execution despite global safety mode being enabled. ## Affected versions `<= 0.1.8` (verified on current upstream HEAD as of 2026-03-03) ## Non-duplication check against published Ficklin
O3 Security · Impact-Aware SCA

Is GHSA-wccx-j62j-r448 in your dependencies?

O3 Security finds GHSA-wccx-j62j-r448 across PyPI dependencies, including transitive ones, and its impact-aware SCA ranks findings by whether your code actually calls the vulnerable path.

GHSA-wccx-j62j-r448: fickling | O3 Security