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.
- CWE
- CWE-184
- 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 FalsePatched 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 ::
