Un des participants au concours UIUCTF 2025 a expliqué en détail comment il a réussi à accomplir une tâche exigeant l'exécution de son code sur le serveur, ayant seulement la possibilité de modifier le contenu du texte des commentaires dans le code.
Les participants pouvaient envoyer une requête réseau à un script Python qui créait un nouveau script Python avec un nom aléatoire, ajoutait les données fournies par l'utilisateur dans le texte des commentaires, en coupant les caractères «\n» et «\r», et exécutait ce script avec la commande «python3 nom.py». En contrôlant uniquement le contenu du commentaire, le participant devait extraire une chaîne du fichier «/home/ctfuser/flag». Le script était créé par le code suivant : comment = input(«> «).replace(«\n», «»).replace(«\r», «») code = f»»»print(«hello world!») # Ceci est un commentaire. Voici un autre : # {comment} print(«Merci de jouer !»)»»»
Au lieu de «{comment}», les données fournies par le participant étaient insérées, et au final, le code suivant était exécuté : print(«hello world!») # Ceci est un commentaire. Voici un autre : # Données fournies par le participant au concours print(«Merci de jouer !»)
La tâche a été formulée à partir d'une vulnérabilité dans le parseur CPython, qui traitait le caractère à code nul comme la fin de la ligne (cette vulnérabilité pouvait, par exemple, être utilisée pour masquer des actions malveillantes dans le texte du commentaire). Le problème a été résolu dans les versions CPython 3.12.0 et 3.11.4. Dans le processeur utilisé dans le concours, seuls les caractères «\n» et «\r» étaient coupés, mais en utilisant une version vulnérable de CPython, le participant pouvait utiliser le caractère «\0» comme délimiteur. Cependant, ce truc n'a pas fonctionné car la version corrigée de CPython était déjà utilisée dans le concours, en espérant qu'il puisse exister d'autres erreurs similaires dans le parseur que les participants pourraient découvrir.
Le participant qui a réussi à accomplir la tâche n'a pas cherché de nouvelles vulnérabilités dans le parseur qui permettraient de diviser la chaîne en parties, mais a utilisé une caractéristique de l'exécution des fichiers Python en fonction de leur contenu. Par exemple, au lieu du code source dans un fichier avec l'extension «.py», il est possible d'y placer le bytecode mis en cache, enregistré dans des fichiers avec l'extension «.pyc», et un tel fichier sera exécuté. Dans le concours examiné, le participant pouvait contrôler uniquement le contenu au milieu du fichier, donc il ne pouvait pas ajouter son propre en-tête pour altérer le type MIME.
La tâche a pu être résolue en profitant du fait que Python, à partir de la version 2.6, peut exécuter le contenu des archives ZIP pour livrer des packages Python sous forme compressée. Comme pour le cache de bytecode, la présence d'une archive zip est déterminée par son contenu et non par l'extension du fichier, c'est-à-dire qu'il est possible de placer une archive zip dans «fichier.py» et à l'exécution de la commande «python fichier.py», elle sera traitée comme un package Python compressé. De plus, les archives ZIP dans Python ne sont pas indexées par l'en-tête au début du fichier, mais par la section EOCD (End of Central Directory Record) à la fin du fichier. Si l'archive contient le fichier «__main__.py», ce fichier est exécuté automatiquement lors du lancement direct de l'archive par la commande «python archive».
La tâche du concours a été résolue par la génération d'une archive ZIP similaire et son insertion dans le texte du commentaire. Pour maintenir l'intégrité de la structure du fichier en présence d'un appel à ‘print(«Merci d'avoir joué !»)’ à la fin du fichier source, la présence dans la section EOCD d'un espace de commentaire, placé à la toute fin, a été utilisée.

Source : opennet.ru
