Skip to main content
CVE Vulnerability Database

CVE-2026-3490: picklescan RCE Vulnerability

CVE-2026-3490 is a remote code execution flaw in picklescan before 1.0.4 that allows attackers to bypass blocklists using pkgutil.resolve_name. This post covers technical details, affected versions, and mitigation.

Published:

CVE-2026-3490 Overview

CVE-2026-3490 is a blocklist bypass vulnerability in picklescan versions before 1.0.4. The scanner fails to block pkgutil.resolve_name, which lets attackers resolve any dangerous function through indirect REDUCE opcodes in a pickle stream. An attacker can invoke blocked functions such as os.system, builtins.exec, or subprocess.call to achieve remote code execution. The flaw maps to CWE-183: Permissive List of Allowed Inputs and effectively defeats the entire blocklist enforced by picklescan. Users that rely on picklescan to vet untrusted machine learning model files are exposed to arbitrary code execution when loading those files with pickle, torch.load, or joblib.load.

Critical Impact

A single allowed function (pkgutil.resolve_name) lets attackers reach any Python callable, bypassing the full picklescan blocklist and enabling remote code execution on systems that load scanned pickle files.

Affected Products

  • picklescan versions prior to 1.0.4
  • Python machine learning pipelines that use picklescan to validate untrusted pickle or PyTorch model files
  • CI/CD and model-hosting systems that treat a passing picklescan result as a safety signal

Discovery Timeline

  • 2026-06-17 - CVE-2026-3490 published to NVD
  • 2026-06-17 - Last updated in NVD database

Technical Details for CVE-2026-3490

Vulnerability Analysis

picklescan enforces security by inspecting pickle opcodes and matching imported symbols against a static blocklist of dangerous callables. The blocklist denies obvious primitives such as os.system, subprocess.Popen, builtins.exec, and builtins.eval. The blocklist does not include pkgutil.resolve_name, a standard-library helper that resolves a dotted string into an arbitrary Python object. Because pkgutil.resolve_name is treated as benign, an attacker can craft a pickle that loads it via GLOBAL, then calls it through REDUCE with a string argument such as "os:system". The returned callable is the very function the blocklist intended to deny. A second REDUCE then invokes that resolved callable with attacker-supplied arguments, producing arbitrary code execution at unpickle time.

Root Cause

The root cause is an incomplete denylist. picklescan blocks specific known-dangerous symbols rather than constraining the set of allowed symbols. Any standard-library function that performs dynamic name resolution undermines this model. pkgutil.resolve_name returns arbitrary attributes from arbitrary modules, so allowing it as a callable inside a pickle stream is equivalent to allowing every callable in the Python runtime.

Attack Vector

The attack vector is network-reachable and requires no authentication or user interaction beyond loading an untrusted pickle file. An attacker publishes a malicious model artifact, for example on a model hub, in a shared dataset, or as a CI/CD artifact. A defender scans the file with picklescan and sees a clean result. When the file is later deserialized with pickle.load, torch.load, or joblib.load, the REDUCE opcode resolves os.system (or any other blocked function) through pkgutil.resolve_name and executes the attacker-supplied command under the privileges of the loading process. Refer to the GitHub Security Advisory GHSA-vvpj-8cmc-gx39 and the VulnCheck Advisory on PickleScan for technical details.

Detection Methods for CVE-2026-3490

Indicators of Compromise

  • Pickle or PyTorch artifacts containing a GLOBAL opcode that imports pkgutil with the attribute resolve_name.
  • REDUCE opcodes that pass strings of the form "module:function" or "module.function" to a previously imported callable.
  • Unexpected child processes spawned by Python interpreters that load model files, such as sh, bash, cmd.exe, or powershell.exe.
  • Outbound network connections initiated immediately after a pickle.load, torch.load, or joblib.load call.

Detection Strategies

  • Statically disassemble pickle files with pickletools.dis and alert on any reference to pkgutil.resolve_name, importlib.import_module, operator.attrgetter, or other dynamic resolution primitives.
  • Inventory installed picklescan versions across build agents, data science workstations, and model registries, and flag any version below 1.0.4.
  • Hash and track model files between scan time and load time so that a tampered artifact cannot be substituted after a clean scan.

Monitoring Recommendations

  • Monitor process trees rooted at Python workers that load third-party model files for execution of shell interpreters or system utilities.
  • Log all invocations of pkgutil.resolve_name in production Python services through audit hooks (sys.addaudithook).
  • Correlate model-load events with outbound DNS and HTTP traffic from data science and inference hosts.

How to Mitigate CVE-2026-3490

Immediate Actions Required

  • Upgrade picklescan to version 1.0.4 or later on every host, container image, and CI/CD pipeline that performs pickle validation.
  • Re-scan all previously approved pickle and PyTorch artifacts with the patched version and revoke any that now fail.
  • Quarantine model files received from untrusted sources until they are re-scanned and, where possible, converted to a safer format such as safetensors.

Patch Information

The maintainers fixed the issue in picklescan1.0.4 by extending the blocklist to cover pkgutil.resolve_name and related dynamic resolution functions. See the GitHub Security Advisory GHSA-vvpj-8cmc-gx39 for the fix commit and release notes.

Workarounds

  • Treat picklescan results as advisory only and do not load pickle files from untrusted publishers.
  • Convert model storage to safetensors or another format that does not execute code during deserialization.
  • Load untrusted pickle files inside a sandbox with no network access, a read-only filesystem, and a non-privileged user.
  • Apply a local pickle.Unpickler subclass that overrides find_class to enforce an explicit allowlist of safe symbols.
bash
# Upgrade picklescan to the fixed release
pip install --upgrade "picklescan>=1.0.4"

# Verify the installed version
python -c "import picklescan, sys; print(picklescan.__version__)"

# Inspect a suspicious pickle for dynamic resolution primitives
python -m pickletools -a suspicious_model.pkl | grep -E "pkgutil|resolve_name|import_module"

Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

Default Legacy - Prefooter | Experience the World’s Most Advanced Cybersecurity Platform

Experience the Most Advanced Cybersecurity Platform

See how the world’s most intelligent, autonomous cybersecurity platform can protect your organization today and into the future.