Trouver le point d'exécution d'outil : RCE via un agent LLM
Le défi
Cet agent LLM laisse le modèle appeler des outils en renvoyant du JSON. Un outil exécute le code que le modèle place dans sa réponse, sans aucun bac à sable - une sortie de modèle empoisonnée devient donc une exécution de code à distance. Lisez les deux fichiers et tapez l'appel qui exécute ce code.
Ce que tu vas apprendre
- Reconnaître exec()/eval() sur une sortie de LLM comme un point d'exécution de code à distance
- Suivre les données d'une réponse de modèle à travers l'analyse JSON jusqu'à un aiguilleur d'outils
- Comprendre comment l'injection de prompt dégénère en exécution de code
- Distinguer l'exécution de code (exec) de l'analyse sûre de données (ast.literal_eval)
- Expliquer pourquoi la sortie du modèle doit être traitée comme une entrée non fiable
Compétences testées
Prérequis
- Lecture basique de Python
- Fonctionnement général d'un appel d'outil/fonction LLM
- Ce qu'est l'injection de prompt
Comment ça marche
Les agents à appel d'outils permettent à un modèle d'agir : il renvoie une requête structurée (un nom d'outil et des arguments) et le moteur d'exécution l'exécute. Le problème de sécurité est que la sortie du modèle n'est pas une donnée de confiance. Un attaquant peut l'orienter par injection de prompt, une instruction malveillante cachée dans un message utilisateur, une page web ou un document récupéré, si bien que tout outil qui exécute la sortie brute du modèle est un point d'injection.
Dans agent.py, run_tool lit le nom de l'outil dans le JSON du modèle et, pour l'outil python, appelle exec(args['code']). Quelle que soit la chaîne que le modèle place dans args['code'], elle est exécutée comme du Python sur l'hôte, sans aucun bac à sable et avec tous les privilèges du processus. Un prompt empoisonné peut amener le modèle à émettre {"tool": "python", "args": {"code": "__import__('os').system('...')"}} et l'agent l'exécute : il s'agit d'une exécution de code à distance qui provient d'un texte, et non d'un port réseau.
Le contraste se trouve dans tools.py. parse_number utilise ast.literal_eval, qui convertit une chaîne en nombre, liste ou dictionnaire et refuse tout ce qui est exécutable. C'est la bonne primitive quand on a besoin de données à partir de texte. Le travail du relecteur consiste à suivre la réponse du modèle jusqu'à l'appel qui l'exécute, et cet appel est exec.
Erreurs fréquentes
- Pointer vers json.loads. Analyser la réponse est correct ; le bug consiste à exécuter l'un de ses champs. Nommez l'appel qui exécute le code.
- Choisir ast.literal_eval. C'est l'assistant sûr de tools.py : il analyse des données et n'exécute jamais rien.
- Supposer que le prompt système vous protège. On peut détourner un modèle de n'importe quelle instruction ; le moteur d'exécution ne doit pas faire confiance à sa sortie.
- Penser que « c'est juste un outil de calcul ». L'outil python exécute du code arbitraire, pas de l'arithmétique : le nom de la fonctionnalité masque le point d'injection.
Comment s'en protéger
Ne transmettez jamais la sortie du modèle à exec() ou eval(). Traitez chaque appel d'outil du modèle comme contrôlé par un attaquant et limitez ce que les outils peuvent faire.
- Remplacez un outil de code brut par une liste d'autorisation fixe d'outils typés et paramétrés (pas de code libre).
- Si l'exécution de code est vraiment nécessaire, faites-la tourner dans un bac à sable strict (conteneur séparé, sans réseau, privilèges réduits, limites de ressources).
- Validez et vérifiez le schéma des arguments de l'outil avant l'aiguillage ; rejetez tout ce qui est inattendu.
- Utilisez
ast.literal_evaloujson.loadsquand vous avez seulement besoin de transformer du texte en donnée.
Solution complète
Statistiques de la communauté
Hacks du jour associés
Contributeurs crédités
Ces hackers ont signalé des problèmes, amélioré le contenu et renforcé cette page.