VAITP Dataset

← Back to the dataset

CVE-2026-53873

picklescan incomplete blocklist for profile.run() allows code execution.

  • CVSS 9.3
  • CWE-184
  • Input Validation and Sanitization
  • Remote

picklescan before 1.0.4 contains an incomplete blocklist for the profile module that fails to block the module-level profile.run() function, allowing attackers to achieve arbitrary code execution via exec(). Attackers can craft malicious pickle files calling profile.run(statement) to execute arbitrary Python code while picklescan reports zero security issues.

CVSS base score
9.3
Published
2026-06-17
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 1.0.4 or later.

Vulnerable code sample

import pickletools

def vulnerable_scan_pickle_data(data: bytes):
    """
    A simplified representation of a vulnerable scanner using an incomplete blocklist.
    This version is vulnerable because ('profile', 'run') is missing.
    Returns True if a known dangerous global is found, otherwise False.
    """
    # This blocklist is incomplete and crucially misses the `profile.run` function.
    DANGEROUS_GLOBALS = {
        ('os', 'system'),
        ('subprocess', 'run'),
        ('builtins', 'eval'),
        ('builtins', 'exec'),
    }

    for opcode, arg, _ in pickletools.genops(data):
        if opcode.name == 'GLOBAL':
            try:
                module, name = arg.split('\n', 1)
            except ValueError:
                continue

            if (module, name) in DANGEROUS_GLOBALS:
                # A known dangerous global was found.
                return True

    # No globals from the incomplete blocklist were found.
    # This will incorrectly return False for a pickle using `profile.run`,
    # thus reporting zero security issues.
    return False

Patched code sample

import pickle
import io

# This code represents the logic used in a fixed version of picklescan (>= 1.0.4)
# to correctly identify the dangerous 'profile.run' function.
# The vulnerability (CVE-2023-53873) was an incomplete blocklist.
# The fix was to add 'profile.run' to that blocklist.

# A simplified representation of the blocklist *after* the fix.
FIXED_DANGEROUS_GLOBALS = {
    "os.system",
    "subprocess.run",
    "eval",
    "exec",
    # ... many other dangerous built-ins and module functions
    "profile.run",  # This is the key addition that fixes the vulnerability.
}

def mock_picklescan_scanner(pickle_data):
    """
    A simplified scanner that checks if a global function call found in a pickle
    is present in the blocklist. This simulates the core logic of picklescan.
    """
    issues_found = 0
    # In a real scanner, this would involve parsing the pickle opcodes.
    # We simulate finding a dangerous global call for demonstration.
    # Let's pretend the unpickler found the following globals.
    found_globals = ["profile.run", "math.sqrt"] 

    print("--- Simulating scan with a fixed blocklist ---")
    for found_global in found_globals:
        if found_global in FIXED_DANGEROUS_GLOBALS:
            print(f"DANGEROUS: Found blocked global '{found_global}'.")
            issues_found += 1
        else:
            print(f"SAFE: Global '{found_global}' is not in the blocklist.")
    
    print("---------------------------------------------")
    if issues_found > 0:
        print(f"Scan complete. Security issues found: {issues_found}")
    else:
        print("Scan complete. Zero security issues found.")

if __name__ == "__main__":
    # A vulnerable scanner would incorrectly report "Zero security issues" for a
    # pickle containing `profile.run`.
    # This fixed scanner correctly identifies it as dangerous.
    
    # We don't need to create a real malicious pickle; we can just simulate
    # the scanner's logic on the name of the dangerous function.
    mock_picklescan_scanner(b"dummy pickle data")

Payload

b'cprofile\nrun\n(S\'import os; os.system("id")\'\ntR.'

Cite this entry

@misc{vaitp:cve202653873,
  title        = {{picklescan incomplete blocklist for profile.run() allows code execution.}},
  author       = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
  year         = {2026},
  note         = {VAITP Python Vulnerability Dataset, entry CVE-2026-53873},
  howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-53873/}}
}
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 ::