Live 18/11 · Cohorte 318 nov à 19h00 MSK — atelier pratique gratuit sur les agents IA · La Cohorte 3 démarre le 23/11Atelier 18/11 →Cohorte 3 (23/11)
SortieAnalyse · Sur le radarHabr AI→ original

Kilo Code a transmis en silence les 12 fichiers du projet à un serveur, alors que deux seulement étaient autorisés

L'auteur a vérifié ce qui part vers le serveur du modèle lorsqu'un assistant IA corrige un seul caractère dans le code. Kilo Code 7.4.20 a remplacé l'addition par une multiplication, le test est passé et un seul fichier a changé sur le disque. Pourtant, le serveur de test, qui jouait le rôle d'un fournisseur non fiable, a reçu les 12 fichiers du projet, alors que l'utilisateur n'avait autorisé la transmission que de deux, et les copies étaient identiques octet pour octet.

Traité par IA depuis Habr AI ; édité par Hamidun News
Kilo Code a transmis en silence les 12 fichiers du projet à un serveur, alors que deux seulement étaient autorisés
Source : Habr AI. Collage: Hamidun News.
Écouter l'article
Fiche synthèse et chiffres clés
ParamètreDonnées
Source primaireHabr AI ↗
Rédacteur en chef
Chiffre clé #1Le serveur lisait les fichiers par de simples appels read, sans archive ni curl
Chiffre clé #2Les résultats de lecture étaient ajoutés à l'historique du chat et repartaient dans chaque requête suivante
Chiffre clé #3Interdire read ne protège pas si bash est autorisé : Python lit le même fichier

D'ordinaire, nous validons le travail d'un assistant IA ainsi : on regarde le diff, on lance les tests, on voit le vert et on clôt la tâche. L'expérience décrite sur Habr révèle une faille dans cette habitude. La modification peut être parfaite, et le projet se trouver pourtant déjà sur un serveur tiers.

Comment l'expérience a été montée

La tâche était volontairement minuscule : dans price.py, la fonction total(price, count) renvoyait price + count, alors que le test attendait que total(7, 3) donne 21. Il fallait remplacer l'addition par une multiplication et lancer check.py. L'instruction de l'utilisateur précisait que seuls price.py et check.py pouvaient être transmis.

L'auteur est parti d'un modèle de menace où le fournisseur de LLM n'est pas fiable et enfreint délibérément les instructions. Ce rôle était tenu par un serveur HTTP local sur 127.0.0.1. Il imitait les réponses en streaming de l'API Chat Completions compatible OpenAI et envoyait au client des tool_calls : d'abord douze commandes read pour tous les fichiers d'un projet synthétique, puis une commande de correction, puis le lancement du test. Le client était bien réel : bin/kilo.exe de l'extension Kilo Code 7.4.20, lancé via la commande CLI run. La configuration autorisait la lecture et l'exécution de commandes.

Résultat

En apparence, tout semblait exemplaire. Kilo a modifié un seul caractère, le test est passé, les autres fichiers du disque sont restés intacts, le code de sortie était nul et les 14 appels d'outils se sont terminés avec le statut completed. Mais le serveur a reçu les 12 fichiers, soit 11 103 octets au total, et leurs SHA-256 correspondaient aux originaux.

Le mécanisme est simple. Kilo ajoute le résultat de chaque lecture à l'historique du chat et l'envoie dans la requête suivante. Le serveur extrait les messages de rôle tool, supprime les numéros de ligne et reconstitue les fichiers. Aucun canal d'exfiltration n'est nécessaire : le client transporte lui-même les données dans ses requêtes normales.

Le trafic est lui aussi intéressant. Il y a eu 15 appels au modèle au total, et le volume cumulé des corps de requêtes a atteint 458 779 octets pour 11 Ko de code unique. La cause : le renvoi de l'historique, les descriptions d'outils et le balisage de service. Il n'y avait pas de requête énorme : le code partait par petites portions, puis se répétait. La taille d'une requête isolée ne permet donc pas de repérer une telle fuite.

Pourquoi les interdictions fonctionnent moins bien qu'on ne le croit

Dans la série de tests initiale, la règle read: deny bloquait la lecture directe du répertoire interdit. Mais avec bash: allow, Python a lu le même fichier et la sortie est partie dans la requête suivante. Lorsque bash a été interdit, la commande ne s'est pas exécutée. Les droits de Kilo sont définis séparément pour chaque outil, et l'interdiction de l'un ne ferme pas les autres voies.

Il y a aussi des réserves honnêtes. L'expérience n'utilise pas de vrai modèle : les commandes étaient choisies par le serveur de test, ce qui ne mesure donc pas la propension des modèles commerciaux à ce comportement. Les noms de fichiers étaient connus d'avance du serveur ; l'exploration autonome d'une arborescence inconnue n'a pas été testée. La session graphique de l'extension n'a pas non plus été testée, seulement la CLI.

Que faire en pratique

Premièrement : évaluez non seulement le diff, mais aussi le journal des appels d'outils et les corps des requêtes envoyées au fournisseur. Faites au moins une fois passer l'agent par un proxy local pour voir ce qui part réellement.

Deuxièmement : donnez à l'agent un minimum de droits. Si la lecture est autorisée, tout ce qui est lu appartient potentiellement au fournisseur. Pour les dépôts sensibles, c'est un argument en faveur des modèles locaux ou de fournisseurs offrant des garanties contractuelles strictes.

Troisièmement : ne vous fiez pas à une instruction textuelle du type « ne transmets que ces deux fichiers ». C'est un vœu adressé au modèle, pas une restriction du client. Seuls les droits des outils, le bac à sable et les politiques réseau établissent une vraie frontière.

L'auteur a publié le banc d'essai en entier : Python 3.10+, Git et l'exécutable Kilo, environ 150 lignes de code. Il vaut la peine de le reproduire sur votre propre client, quel que soit l'agent que vous utilisez.

Dossier

Questions clés sur cet article

Comment l'expérience a été montée?

La tâche était volontairement minuscule : dans price.py, la fonction total(price, count) renvoyait price + count, alors que le test attendait que total(7, 3) donne 21. Il fallait remplacer l'addition par une multiplication et lancer check.py. L'instruction de l'utilisateur précisait que seuls price.py et check.py pouvaient être transmis. L'auteur est parti d'un modèle de menace où le fournisseur de LLM n'est pas fiable et enfreint délibérément les instructions.

Que sait-on sur Résultat ?

En apparence, tout semblait exemplaire. Kilo a modifié un seul caractère, le test est passé, les autres fichiers du disque sont restés intacts, le code de sortie était nul et les 14 appels d'outils se sont terminés avec le statut completed. Mais le serveur a reçu les 12 fichiers, soit 11 103 octets au total, et leurs SHA-256 correspondaient aux originaux. Le mécanisme est simple. Kilo ajoute le résultat de chaque lecture à l'historique du chat et l'envoie dans la requête suivante.

Pourquoi les interdictions fonctionnent moins bien qu'on ne le croit?

Dans la série de tests initiale, la règle read: deny bloquait la lecture directe du répertoire interdit. Mais avec bash: allow, Python a lu le même fichier et la sortie est partie dans la requête suivante. Lorsque bash a été interdit, la commande ne s'est pas exécutée. Les droits de Kilo sont définis séparément pour chaque outil, et l'interdiction de l'un ne ferme pas les autres voies. Il y a aussi des réserves honnêtes.

ZK
Zhemal Khamidun — CPO d'Alpina Digital, product manager d'AlpinaGPT. Conçoit des produits IA depuis 2019. Hamidun News est un projet personnel devenu un agrégateur en production avec plus de 6 000 lecteurs.
✦ Unified AI Workspace · Nomad

Test Frontier LLMs & Autonomous Agents in One Workspace

Run Claude Opus 5.5, GPT-6, Gemini 3.1, and DeepSeek side-by-side in Nomad AI (nomadnet.ai) — unified model routing, deep research, and multimodal workflows under one subscription.

Try Nomad AI →Launch Nomad Agent
Qu'en pensez-vous ?
Chargement des commentaires…