système : OPÉRATIONNEL
← retour à tous les hacks
AGENTS CRITICAL NEW

DeepSeek Harness : l'agent confiné qui pouvait désactiver son propre bac à sable

Un agent de code confiné pouvait lever son propre confinement avec une seule commande shell, parce que le harnais décidait de la confiance à partir d'un en-tête fourni par l'appelant.

2026-09-16 // 8 min affects: deepseek-harness, coding-agents, llm-agents, deepseek

De quoi s’agit-il ?

Le 8 septembre 2026, Nir Zadok et Moshe Siman Tov Bustan, d’OX Research, ont publié leur divulgation d’une faille dans DeepSeek Harness (dsh), le harnais open source et local de DeepSeek pour exécuter des agents de code sur la machine d’un développeur. Un agent confiné pouvait désactiver son propre bac à sable avec une seule commande shell, sur la configuration livrée par défaut, sans exposition réseau ni identifiants. VulnCheck, en tant que CNA, a publié la fiche le même jour avec une note de 9,4.

Le harnais est sorti en août 2026 et a dépassé 215 000 étoiles GitHub en quelques semaines. Il propose une interface navigateur adossée à une API HTTP locale, et exécute les commandes de l’agent dans un bac à sable système — bubblewrap, Landlock ou Seatbelt selon la plateforme — précisément pour qu’un agent manipulant du contenu non fiable ne puisse pas sortir de son espace de travail.

Le point intéressant n’est pas que le bac à sable ait eu un trou. C’est que le plan de contrôle du bac à sable se trouvait à l’intérieur du bac à sable.

Comment ça marche

Trois choix de conception ordinaires, chacun défendable isolément, se sont composés en une évasion.

Le bac à sable confinait les fichiers, pas le réseau. L’appel à bubblewrap passait --unshare-pid mais pas --unshare-net : l’espace de noms réseau restait partagé avec l’hôte. Le profil Seatbelt était (allow default) avec (deny file-write*) par-dessus. La boucle locale était donc joignable depuis l’intérieur. La référence CLI du projet pour la version affectée indique explicitement que, dans la configuration par défaut, les lectures et l’accès réseau ne sont pas confinés.

L’API locale déterminait la confiance à partir d’un en-tête fourni par le client. Une seule fonction, isTrustedApiRequest, lisait l’en-tête Host et accordait l’accès si la valeur correspondait à une autorité de boucle locale ou figurait dans une liste trustedHosts. Elle ne comparait jamais cette valeur à l’adresse réelle du pair. Contre la menace pour laquelle elle avait été écrite — le DNS rebinding, où un navigateur ne peut pas mentir sur Host — elle fonctionnait. Contre un client non soumis aux règles du navigateur, non. Un commentaire dans le fichier le dit d’ailleurs : ce contrôle, selon les mots des auteurs, « is not an auth layer » (n’est pas une couche d’authentification).

Les appels shell ordinaires ne demandaient aucune approbation. L’approbation ne se déclenchait que lorsqu’une commande demandait plus d’accès que la session n’en détenait déjà. L’évasion ne demandait rien : elle modifiait le réglage de la session.

agent (dans le bac à sable)
   │  appel d'outil bash — aucune approbation requise

curl → http://127.0.0.1:<port>/…   Host: <autorité de boucle locale>
   │  isTrustedApiRequest() lit l'en-tête, pas le pair

mode de session := danger-full-access,  approbation := never


toute commande ultérieure s'exécute hors confinement, sans invite

Le harnais fournit par ailleurs au shell de l’agent l’adresse de cette interface et l’identifiant de la session courante : aucune phase de découverte n’est nécessaire.

Deux détails du journal de session relevé par OX méritent qu’on s’y arrête. Aucun événement approval/request n’y figure. Et le changement de politique est attribué à source: {kind: 'user'}le harnais ne savait pas distinguer le shell de son propre agent de l’humain au clavier. OX a également mené un contrôle apparié : deux sessions, même préréglage par défaut, même écriture vers un chemin hors de l’espace de travail. Celle qui avait passé l’appel a réussi ; l’autre a été bloquée. Le bac à sable fonctionnait jusqu’au moment où on lui a demandé de s’arrêter.

Un second chemin existait partout où le port était joignable de l’extérieur — tunnel, reverse proxy, redirection SSH, port forward d’éditeur. Un appelant distant non authentifié pouvait alors piloter l’agent et télécharger l’intégralité des conversations stockées, sans clé d’API ni appel au modèle.

Pourquoi c’est important

La seule condition préalable à l’évasion locale était que l’agent exécute une commande induite par un texte fourni par l’attaquant. C’est exactement la classe d’entrées que le bac à sable existe pour contenir. Le contrôle a donc été défait par ce contre quoi il avait été construit — ce qui en fait un échec de confinement et non de filtrage, or le confinement est la couche sur laquelle on se replie quand le filtrage échoue.

Un harnais d’agent de code vaut d’être attaqué parce qu’il détient un shell s’exécutant avec l’autorité ambiante du développeur qui l’a lancé : arborescences de sources, commandes de build et de test, et tout ce que ce poste peut atteindre en clés SSH, identifiants cloud et systèmes internes. Nous avons couvert des variantes voisines : localhost comme surface d’attaque des agents, le DNS rebinding contre les serveurs MCP locaux, la frontière de privilège harnais/modèle, et la configuration de dépôt qui exécute du code attaquant hors du bac à sable. Le motif se répète parce que l’agent réside sur le même hôte que ce qui le gouverne.

Deux éléments de gestion méritent d’être signalés. Des membres de la communauté ont décrit cette même évasion sur le forum public du projet les 13 et 14 août 2026, onze jours avant le signalement par la voie éditeur. Et la version corrigée 0.1.2-alpha.1 (27 août) n’a jamais été publiée sur npm, là où les instructions du projet envoient pourtant les utilisateurs ; la première version corrigée sur npm est 0.1.2-alpha.2, le 30 août. Un correctif qui n’atteint pas le chemin d’installation n’est pas encore un correctif.

Le fichier SAFETY.md du projet indique que le logiciel n’a pas fait l’objet d’un audit de sécurité et que le bac à sable et les invites d’approbation ne garantissent pas l’isolation. Cet avertissement est honnête, et il faut le lire comme une spécification plutôt que comme une formule d’usage.

Défenses

Placez le plan de contrôle de l’agent hors de sa portée. Si l’API capable de modifier le niveau de privilège d’une session est joignable depuis l’intérieur du bac à sable, le bac à sable n’est qu’indicatif. Isolez l’espace de noms réseau (--unshare-net sous bubblewrap, un deny network* explicite sous Seatbelt) ou liez l’API de contrôle à un socket que le profil refuse. C’est le correctif qui se généralise au-delà d’un seul produit.

Ne dérivez jamais la confiance d’un en-tête contrôlé par le client. Host, Origin et X-Forwarded-For sont fournis par l’appelant. « Local uniquement » signifie vérifier l’adresse du pair — ou mieux, exiger un secret, ce que fait le correctif : un jeton à usage unique affiché au démarrage, échangé contre un cookie signé que chaque appel doit porter.

Contrôlez le changement de privilège, pas seulement l’acte privilégié. Une approbation qui se déclenche quand une commande demande plus d’accès, mais pas quand un appel l’accorde, laisse tout le chemin d’escalade sans surveillance. Traitez toute modification du mode de bac à sable, de la politique d’approbation ou d’une liste d’autorisation comme l’action la plus sensible du système.

Rendez l’acteur distinguable dans votre piste d’audit. Un changement de politique enregistré comme venant de « l’utilisateur » alors qu’il vient du shell d’un agent met en échec la détection autant que l’investigation. Attachez un principal distinct aux appels d’origine agentique, et alertez sur tout changement de privilège portant ce principal.

Auditez ce que vous exécutez déjà. Passez à une version postérieure au correctif ; vérifiez quelle version du harnais embarque un éventuel wrapper de bureau tiers, les mainteneurs de wrappers épinglant leurs propres copies. Supprimez tunnels, proxies et redirections de port d’éditeur qui exposent l’interface locale d’un agent. Et là où vous ne pouvez pas vérifier le confinement, considérez qu’une session compromise détient l’autorité complète du développeur, et dimensionnez vos identifiants en conséquence.

Notez ce que le correctif ne change pas : le bac à sable ne confine toujours ni les lectures ni l’accès réseau, et le shell de l’agent reçoit toujours l’adresse de l’interface. L’authentification a fermé la porte qui était ouverte ; elle n’a pas déplacé la porte hors de la pièce.

Statut

ÉlémentDétail
RéférenceCVE-2026-82533 (VulnCheck en tant que CNA), CWE-807 — confiance en des entrées non fiables pour une décision de sécurité
SévéritéCVSS 9,4
AffectéDeepSeek Harness (dsh) 0.1.1-rc.2 et antérieurs
Signalements communautairesRapports publics sur le forum du projet, 13 et 14 août 2026
DivulgationSignalé à VulnCheck le 24 août 2026
Correctif0.1.2-alpha.1, 27 août 2026 (GitHub uniquement) ; première version corrigée sur npm 0.1.2-alpha.2, 30 août 2026
VérificationRe-test et confirmation de la remédiation par OX Research, 30 août 2026
PublicationFiche CVE et analyse OX, 8 septembre 2026
Lacune résiduelleLe bac à sable ne confine toujours pas les lectures ni le réseau ; le shell de l’agent reçoit toujours l’adresse de l’interface de contrôle

Sources