Lire un secret divulgué dans un system prompt LLM
Le défi
Vous avez un shell sur une machine qui exécute un chatbot d'entreprise. L'opérateur a écrit un secret directement dans le prompt système du modèle, pensant que les utilisateurs ne le verraient jamais. Trouvez le fichier du prompt système sur le disque, lisez-le et soumettez le secret intégré à l'intérieur.
Ce que tu vas apprendre
- Repérer d'où un serveur d'inférence charge son system prompt
- Suivre un chemin de configuration jusqu'au fichier system prompt sur le disque
- Lire un secret intégré par un opérateur dans le texte du prompt
- Utiliser grep pour trouver une valeur marquée dans le répertoire du modèle
- Expliquer pourquoi un system prompt ne peut pas stocker de secrets en toute sécurité
Compétences testées
Prérequis
- À l'aise avec les commandes shell de base (ls, cat, grep)
- Savoir qu'un LLM est guidé par un system prompt
- Comprendre qu'un fichier de configuration peut pointer vers d'autres fichiers
Comment ça marche
Un system prompt est l'instruction permanente qu'une application ajoute au début de chaque conversation avec un modèle de langage : ton, règles et contexte que le modèle doit suivre. Comme l'utilisateur final ne le tape jamais et que l'interface de chat ne l'affiche jamais, certains opérateurs le traitent comme un canal privé et y glissent des secrets : flags internes, clés API, codes d'escalade. Cette hypothèse est la vulnérabilité que ce challenge illustre.
Le system prompt doit bien provenir de quelque part, et sur un déploiement réel, c'est presque toujours un fichier que le serveur lit au démarrage. Ici, config.json indique system_prompt_path: /opt/model/system_prompt.txt, et serve.py ouvre exactement ce chemin. Quiconque dispose d'un accès en lecture à l'hôte d'inférence peut faire cat sur le fichier et voir chaque mot que l'opérateur pensait caché, y compris la ligne The internal escalation flag is HDNA{prompts_are_not_secrets}.
Même sans shell, un system prompt reste sujet à la fuite. Les modèles peuvent être amenés à répéter leurs instructions via du prompt-injection ou de simples demandes de type ingénierie sociale, si bien qu'un secret placé dans le prompt n'est jamais qu'à un message astucieux de n'importe quel utilisateur du chatbot. La leçon est simple : un system prompt guide le comportement, il ne garde pas de secrets. Tout ce qui y est placé doit être considéré comme lisible aussi bien par les opérateurs de l'hôte que, en pratique, par les utilisateurs du modèle.
Erreurs fréquentes
- Considérer le system prompt comme privé. Les utilisateurs ne le tapent jamais, mais c'est un simple fichier sur le disque, et il peut être extrait du modèle par ruse, il ne cache rien.
- Ouvrir les poids plutôt que le prompt. Le flag est du texte dans
system_prompt.txt, pas dans le binairemodel.safetensors. - Manquer le pointeur de configuration.
config.jsondésignesystem_prompt_path; l'ignorer revient à deviner où se trouve le prompt. - Lire au-delà de la ligne marquée. L'opérateur a désigné la valeur comme le flag d'escalade interne, cette ligne est la réponse, pas les horaires de support juste à côté.
Comment s'en protéger
Les secrets ne doivent jamais se trouver dans un prompt. Les corrections consistent à garder les identifiants entièrement hors du contexte du modèle :
- Stockez les vrais secrets dans un gestionnaire de secrets ou une variable d'environnement, et référencez-les depuis le code applicatif, jamais dans le texte du system prompt.
- Faites appliquer les actions sensibles côté serveur par l'application : le modèle doit demander au backend de vérifier l'identité du personnel, pas détenir un flag d'escalade qu'il pourrait révéler.
- Restreignez l'accès en lecture à l'hôte d'inférence et au fichier de prompt, afin qu'un point d'entrée n'expose pas immédiatement le prompt.
- Ajoutez des tests de prompt-injection aux vérifications de mise en production, afin de vérifier que le modèle ne divulgue pas ses instructions sur demande.
Si un secret a été placé dans un prompt, changez-le et retirez-le du prompt : considérez qu'il a déjà été lu à la fois par toute personne ayant accès à l'hôte et par tout utilisateur qui a su le demander correctement au modèle.