HIGH 8.8

CVE-2026-14535: Trail of Bits fickling Security Analysis Bypass (CVSS 8.8)

Trail of Bits fickling is a Python library designed to safely deserialize pickle files by analyzing them for dangerous imports before unpickling. A logic flaw in versions up to 0.1.11 breaks one of its two main safety checks. When fickling examines a pickle file, it runs two separate inspection passes: one flags obviously dangerous imports, and a second one (MLAllowlist) is supposed to catch sneaky imports from non-ML libraries that the first pass missed. However, the first pass leaves markers in shared memory that trick the second pass into skipping its checks entirely. As a result, any import from Python's standard library—except those on a small blocklist—gets a green light, even if it's dangerous. Because fickling's public API treats a green light as permission to actually deserialize and run the pickle, an attacker can craft a pickle file that imports and executes arbitrary standard library code, bypassing the security gate entirely.

Source data · NVD / CISA · public domain

CVSS
3.1 · 8.8 HIGH · CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
Weaknesses (CWE)
CWE-693
Affected products
1 configuration(s)
Published / Modified
2026-07-04 / 2026-07-10

NVD description (verbatim)

In Trail of Bits fickling versions up to and including 0.1.11, the UnsafeImportsML analysis pass unconditionally calls AnalysisContext.shorten_code(node) on every import node it inspects, regardless of whether the import is flagged as unsafe. This call registers the shortened code representation in the shared AnalysisContext.reported_shortened_code set. When the MLAllowlist analysis pass subsequently runs, it calls the same shorten_code() method, receives already_reported=True for every import, and executes a continue statement that skips its allowlist check entirely. This renders MLAllowlist dead code for all imports — it never evaluates whether an import is in the ML allowlist or not. The MLAllowlist pass was designed to catch imports of modules outside the known-safe ML ecosystem (torch, numpy, transformers, etc.) that slip past the UnsafeImports denylist. With MLAllowlist inoperative, any standard library module not in the UNSAFE_IMPORTS denylist can be invoked via pickle deserialization while fickling's check_safety() returns LIKELY_SAFE. The fickling.load() API chains check_safety() into pickle.loads() as an explicit security gate, meaning a LIKELY_SAFE verdict causes the payload to be deserialized and executed. The root cause is shared mutable state between independently-correct analysis passes — UnsafeImportsML works as designed in isolation, MLAllowlist works as designed in isolation, but the shared reported_shortened_code set causes UnsafeImportsML to poison MLAllowlist's deduplication logic.

5 reference(s) · View on NVD →

SEC.co analysis · AI-assisted, reviewed against source

Technical summary

The vulnerability stems from shared mutable state between two analysis passes in fickling's static analysis pipeline. The UnsafeImportsML pass correctly identifies unsafe imports but unconditionally calls AnalysisContext.shorten_code(node) on every import node, which registers the code representation in reported_shortened_code. When MLAllowlist subsequently runs, it calls the same shorten_code() method and receives already_reported=True for all imports due to the prior registration. This causes MLAllowlist to execute a continue statement that skips allowlist validation entirely, rendering the pass inoperative. MLAllowlist was designed as a secondary gate to reject imports outside the known-safe ML ecosystem (torch, numpy, transformers, etc.) that escape the UnsafeImports denylist. With MLAllowlist dead code, standard library imports not explicitly in UNSAFE_IMPORTS—which includes many modules capable of code execution (subprocess, os, importlib, etc.)—pass check_safety() with a LIKELY_SAFE verdict. Since fickling.load() chains check_safety() as a pickle.loads() wrapper, this verdict causes deserialization and execution. The root cause is poor isolation between otherwise-correct analysis components sharing a deduplication cache.

Business impact

For any organization using fickling as a security boundary for untrusted pickle deserialization workflows, this vulnerability eliminates effective protection. Pickle deserialization with fickling check_safety() is commonly proposed for supply chain security, ML model validation, and data pipeline integrity checks. An attacker with the ability to supply pickle files—via compromised dependencies, malicious model repositories, or poisoned data feeds—can achieve arbitrary code execution while appearing to pass fickling's safety assessment. This is particularly severe in scenarios where fickling's verdict is used to make trust decisions without additional sandboxing. The impact spans any organization relying on fickling for security-critical unpickling operations.

Affected systems

Trail of Bits fickling versions up to and including 0.1.11 are affected. The vulnerability affects all code that uses fickling.load() or fickling.check_safety() to validate pickle files prior to deserialization. Any Python environment with a vulnerable version installed that processes untrusted or semi-trusted pickle inputs is at risk, particularly in ML/data science workflows, model serving pipelines, and automated data ingestion systems.

Exploitability

The vulnerability is highly exploitable. Exploitation requires crafting a pickle file with an import from the Python standard library (outside the small UNSAFE_IMPORTS blocklist) that performs malicious actions—such as os.system(), subprocess calls, or __import__ chains. This is straightforward given pickle's serialization format and publicly available tools. The CVSS vector (CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H) reflects network accessibility, low complexity, no privilege requirement, and high impact across confidentiality, integrity, and availability. User interaction is required only insofar as a victim must deserialize the malicious pickle via an application using fickling—a low barrier in automated or trust-chain scenarios. No authentication is needed. Proof-of-concept exploitation is practical and does not require advanced techniques.

Remediation

Upgrade fickling to a patched version that resolves the shared state corruption between analysis passes. Verify the vendor advisory for the specific fixed version number. Immediate mitigations pending patch deployment include: (1) avoid relying solely on fickling.check_safety() as a security gate for untrusted pickle inputs; (2) layer additional controls such as strict allowlisting of expected import types, sandboxed deserialization, or code review of pickle files before processing; (3) consider alternative serialization formats (JSON, Protocol Buffers, MessagePack) for untrusted data; (4) implement network segmentation and process isolation to limit the blast radius of pickle deserialization in sensitive pipelines.

Patch guidance

Consult the official Trail of Bits advisory and release notes for the patched fickling version. Apply the patch to all systems and dependencies that include fickling, including transitive dependencies pulled in by ML or data science frameworks. Verify that the patch addresses the shared reported_shortened_code state issue and restores MLAllowlist functionality. Test patched versions in a staging environment with previously-malicious test pickle files to confirm that MLAllowlist now correctly rejects non-allowlisted imports. Update any CI/CD pipelines, container images, and dependency lock files to enforce the minimum patched version.

Detection guidance

Monitor for pickle deserialization events involving fickling.load() or check_safety() calls on untrusted inputs. Log and alert on any pickle files that trigger UnsafeImportsML warnings or imports of standard library modules with known code-execution risk (os, subprocess, importlib, etc.). Track fickling version in deployed software bills of materials (SBOMs) and flag instances of 0.1.11 or earlier. If available, enable verbose logging in fickling's analysis passes to observe whether MLAllowlist is being skipped (should show allow-list checks for all imports in patched versions). Hunt for suspicious pickle artifacts in data pipelines, model repositories, and artifact caches.

Why prioritize this

This vulnerability merits immediate high priority: (1) it is a logic bypass that renders a security control completely inoperative for an entire analysis pass, not a minor weakness; (2) it affects the public API surface (fickling.load()) directly used in security-critical deserialization workflows; (3) exploitation is trivial and requires only crafting a standard library import in a pickle file; (4) the impact is arbitrary code execution with no sandboxing required; (5) organizations using fickling specifically chose it as a security boundary, making the bypass a betrayal of expected trust; (6) affected versions (through 0.1.11) are in active use in ML/data science ecosystems. Patch and detection should be prioritized alongside other critical code execution flaws.

Risk score, explained

The CVSS 3.1 score of 8.8 (HIGH) correctly reflects: AV:N (network-accessible via pickle file distribution), AC:L (trivial exploitation; standard library import + malicious code), PR:N (no privileges required), UI:R (user interaction required only to deserialize; low bar in automated scenarios), S:U (impact is confined to the affected resource, not other systems), C:H/I:H/A:H (arbitrary code execution yields complete compromise of confidentiality, integrity, and availability). The score does not account for the context-specific severity in supply chain and model-serving scenarios, where fickling's role as a trusted security gate elevates practical risk beyond the base score. Organizations should treat this as effectively 9.0+ in contexts where fickling is the sole deserialization safeguard.

Frequently asked questions

If I use fickling just to inspect pickle files without actually deserializing them, am I at risk?

No. The vulnerability manifests only when fickling.load() or check_safety() feeds its verdict into pickle.loads() or pickle.load(). If you use fickling only for analysis and inspection (e.g., to list imports or metadata), the poisoned analysis verdict does not trigger code execution. However, you should still upgrade to eliminate the false sense of security that a LIKELY_SAFE verdict might create if deserialization is ever added downstream.

Can I work around this by manually auditing the imports that fickling reports?

Partially, but unreliably. The core issue is that MLAllowlist is entirely skipped—it never runs. This means fickling will not report many dangerous imports from non-allowlisted standard library modules. You cannot trust fickling's import list or verdict in vulnerable versions. Upgrade, or implement independent import analysis (e.g., pickletools.dis() or custom inspection) as a secondary layer.

Does this affect pickle files I've already deserialized with fickling in the past?

No. The vulnerability is a flaw in fickling's verdict logic, not in the pickle files themselves. If a pickle was already safely deserialized without malicious execution, it remains benign. However, if you relied on a fickling verdict to trust a pickle file and deserialized it, an attacker may have already exploited the false verdict. Audit your pickle sources and pipelines for potential compromise.

What if my dependency management system shows a newer fickling version, but my application still fails to update?

Some ML frameworks or tools may pin fickling to an older version for compatibility. Check your transitive dependencies in your lock file (requirements.txt, Pipenv, poetry.lock, etc.). If an indirect dependency locks fickling ≤0.1.11, file an issue with that upstream project or upgrade to a newer version of the framework that relaxes the constraint. In the interim, use the mitigations listed under remediation_summary.

This analysis is provided for informational purposes and reflects the vulnerability details and CVSS score published by the vendor. Verify all remediation guidance, patch version numbers, and availability against the official Trail of Bits advisory before deployment. SEC.co makes no warranty regarding the accuracy or completeness of third-party vendor advisories. Organizations must conduct their own risk assessment based on their use of fickling and exposure to untrusted pickle inputs. This document does not constitute legal or compliance advice. Source: NVD (public-domain), retrieved 2026-08-13. Analysis generated by SEC.co (claude-haiku-4-5).