Retour

2026/10/26

HTTP2Shell (CVE-2026-111497) : mettre à jour un binaire ne corrige pas les processus en cours

La faille Brotli derrière HTTP2Shell touche Node.js, Bun et Python. Mesurer l’exposition réelle et redémarrer les agents IA sans risque.

Le 21 octobre 2026, Node.js a publié des correctifs pour la CVE-2026-111497, une écriture hors limites dans le décodeur Brotli de Google. Dans les jours qui ont suivi, des avis ont été publiés pour Bun, pour les paquets Python brotli et brotlicffi, pour Envoy et pour Istio, et la presse spécialisée a baptisé la faille Node2Shell, puis HTTP2Shell. Malgré ce surnom, il ne s’agit pas d’un bug du protocole HTTP/2. C’est un bug de décompression, qui compte partout où un logiciel décode des données Brotli qu’un attaquant peut influencer.

Cet article présente ce qui est vérifié, ce qui est seulement possible et comment contrôler vos propres systèmes. Il revient aussi sur une leçon tirée de notre propre réponse : installer un binaire corrigé ne corrige pas un processus qui exécute encore l’ancien. Dans notre environnement, des sessions Claude Code de longue durée ont conservé leurs anciens runtimes embarqués après la mise à jour du Claude Code installé.

Points clés

  • Versions concernées : Brotli upstream 1.0.3 à 1.2.0. Corrigé dans : 1.0.10, 1.1.1 et 1.2.1. Les produits qui embarquent Brotli ont besoin de leur propre version corrigée.
  • L’exposition suppose un chemin réel : des données Brotli influencées par un attaquant qui atteignent un décodeur vulnérable. Un nom de produit ou un accès réseau ne constituent pas à eux seuls une preuve.
  • Contrôlez les processus en cours, pas seulement les paquets installés. Un processus conserve le runtime avec lequel il a démarré jusqu’à son redémarrage.
  • Redémarrez les agents IA avec état un par un, après avoir transféré leurs événements, approbations et état de travail.

1. En quoi consiste la vulnérabilité

Lorsque le décodeur rencontrait un changement de bloc invalide, il signalait « need more input » (données supplémentaires nécessaires) au lieu d’une erreur de format définitive. Un appelant en streaming continuait alors à fournir des octets, que le décodeur ajoutait à un tampon interne de huit octets, en écrivant au-delà de sa fin. L’avis de Node.js indique qu’un attaquant capable de fournir des données Brotli qu’une application décompresse avec node:zlib « peut corrompre la mémoire du processus, ce qui peut entraîner un plantage, la divulgation de la mémoire du processus ou l’exécution de code à distance ».

Les niveaux de gravité diffèrent selon les sources. Node.js la classe High (élevée). L’avis GitHub pour les paquets Python et l’avis d’Envoy la classent Critical (critique), avec un score CVSS 4.0 de 9.2. Ubuntu lui attribue actuellement une priorité Medium (moyenne). Au 26 octobre, l’évaluation de la CISA dans l’enregistrement de la CVE indique l’exploitation comme « none » (aucune), et la CVE ne figure pas au catalogue Known Exploited Vulnerabilities de la CISA. L’avis de Node.js mentionne toutefois l’exécution de code à distance comme conséquence possible : ne considérez donc pas l’absence d’attaques signalées comme une raison d’attendre.

2. Prérequis : quand êtes-vous réellement exposé ?

Les trois conditions doivent être réunies :

  1. Un décodeur vulnérable est chargé dans le processus : une bibliothèque Brotli embarquée ou système, dans la plage concernée, sans correctif ni rétroportage.
  2. Quelque chose l’appelle : une décompression explicite, ou le décodage automatique de Content-Encoding: br.
  3. L’entrée peut être influencée par un attaquant : réponses provenant d’URL arbitraires, fichiers téléversés, corps de requêtes entrantes ou trafic HTTP en clair sur un réseau hostile.

Le décodage automatique est le chemin que les équipes négligent le plus souvent. L’avis de Bun précise que fetch envoie Accept-Encoding: br par défaut et décode la réponse : tout programme Bun qui récupère des URL non fiables dispose donc d’un chemin. En Python, dès que brotli ou brotlicffi est installé, urllib3 décode automatiquement les réponses Content-Encoding: br, et requests annonce aussi br dans ses requêtes. Cela se produit même si votre propre code ne mentionne jamais Brotli. Envoy et Istio ne sont exposés que lorsqu’un décompresseur Brotli est configuré.

3. Concerné de façon confirmée ou seulement possible

Situation au 26 octobre 2026. Consultez les avis indiqués, car la situation évolue rapidement.

ComposantStatutCorrigé dans
Brotli upstream (C)Confirmé : 1.0.3 à 1.2.01.0.10, 1.1.1, 1.2.1
Node.js 22, 24, 26Confirmé22.23.4, 24.22.1, 26.12.1
Node.js 20 et versions antérieures (fin de vie)Non évalué ; Node.js considère les branches en fin de vie comme toujours concernéesPasser à une branche prise en charge
Bun (y compris les exécutables compilés)Confirmé : 1.0.21 à 1.4.31.4.4, et recompiler les applications compilées
Python brotli / brotlicffiConfirmé : à partir de 1.0.31.2.1 / 1.2.1.0
Envoy (décompresseur Brotli configuré)Confirmé1.40.0, 1.39.3, 1.38.6, 1.37.8 (1.36.12 sur la branche 1.36, non prise en charge)
Istio (décompression Brotli activée)Confirmé1.31.2, 1.30.6, 1.29.9 (dernière version 1.29)
Paquet brotli d’UbuntuEn cours d’évaluation (toutes les versions)Pas encore de paquet corrigé
Paquet nodejs d’UbuntuVulnérable sur 20.04 (Brotli embarqué). À partir de 22.04, il utilise le Brotli du système et suit donc la ligne ci-dessusVoir le suivi Ubuntu
Paquet brotli de DebianCorrigé dans bookworm et trixie ; pas encore dans forky1.0.9-2+deb12u1, 1.1.0-2+deb13u1
Chrome, Electron et applications ElectronInconnu : aucune déclaration de l’éditeur trouvéeSurveiller les notes de version de l’éditeur

À propos de la dernière ligne : Chrome et Electron embarquent leurs propres copies de Brotli, et une application Electron embarque aussi un runtime Node.js. Au moment de la rédaction, les dernières versions stables d’Electron (45.0.0, 44.9.3 et 43.8.3, toutes du 21 octobre) indiquent Node.js 24.22.0, et non 24.22.1, et leurs notes ne mentionnent pas cette CVE. Les préversions d’Electron du 23 octobre intègrent Node.js 24.22.1 : un correctif stable semble donc probable, mais il n’est pas encore publié. Une application de bureau basée sur Electron est donc possiblement exposée, et non vulnérable de façon confirmée. Il en va de même pour les clients de messagerie et les outils d’IA de bureau : consultez la déclaration de chaque éditeur avant de les déclarer concernés ou sûrs.

Autre piège : la version de bibliothèque affichée ne prouve rien, dans un sens comme dans l’autre. Un Node.js 22.23.4 corrigé affiche toujours process.versions.brotli en 1.1.0, car le correctif a été rétroporté. Les distributions font de même. Fiez-vous à la version corrigée du produit ou au statut de sécurité de la distribution, pas à la chaîne de version de Brotli.

4. Ce que nous avons trouvé dans notre propre environnement

Nous avons réalisé un inventaire en lecture seule avant toute modification. Voici ce que nous avons observé, de façon anonymisée, en séparant les constats confirmés du risque que nous n’avons pas pu vérifier.

Observations confirmées

  • Le Claude Code installé avait déjà été mis à jour, mais une douzaine de sessions de longue durée exécutaient encore des builds plus anciennes, lancées des semaines plus tôt. Elles n’avaient jamais été redémarrées.
  • Notre premier décompte en avait manqué trois. Elles tournaient sous un processus auxiliaire différent, si bien qu’une recherche par ligne de commande ne les a pas trouvées. Une recherche par exécutable les a trouvées.
  • Les fichiers des anciennes builds avaient déjà été supprimés du disque par l’outil de mise à jour : la version de Bun embarquée dans ces processus était donc illisible.
  • Une installation résiduelle, via un gestionnaire de paquets, d’une ancienne build de Claude Code embarquait Bun 1.4.0. Rien ne l’utilisait en fonctionnement normal, mais un chemin de repli aurait pu le faire. Nous l’avons supprimée après avoir démontré que rien n’en dépendait.
  • Un runtime Node.js en fin de vie et inutilisé était encore installé sur un serveur. Nous l’avons supprimé de la même manière.
  • La build actuelle de Claude Code vers laquelle nous avons migré (2.1.310) contient Bun 1.4.4. Nous l’avons confirmé en inspectant le binaire. Le changelog d’Anthropic ne mentionne pas la CVE : vérifiez donc votre propre build de la même manière.

Non vérifié, traité comme un risque

  • Si les anciennes sessions étaient réellement vulnérables. Leur version de Bun était illisible. Le Bun 1.4.4 corrigé n’est apparu que vers le 22 octobre, après ces builds : nous les avons donc traitées comme concernées.
  • Si un attaquant aurait pu atteindre le décodeur par ces sessions, par exemple en leur faisant récupérer une URL malveillante. Ce chemin est plausible au vu du comportement documenté de fetch dans Bun, mais nous ne l’avons pas démontré.
  • Si d’autres logiciels embarquant leur propre copie de Brotli sont concernés. En l’absence de déclaration de l’éditeur, nous suivons le produit au regard des versions de l’éditeur, plutôt que de le présumer sûr ou vulnérable.

Nous n’avons trouvé aucune trace d’exploitation. Nous ne l’avons pas non plus considéré comme une preuve de sécurité : la solution consistait à supprimer l’exposition, pas à l’écarter par des arguments.

5. Liste de contrôle pour la détection et la correction

Trouver les dépendances

Cherchez Brotli là où il est embarqué, pas seulement là où il est installé comme paquet. Examinez vos SBOM et vos images de conteneurs à la recherche de Node.js, Bun, paquets Python, Envoy et Brotli lié statiquement. Vérifiez les dépendances Python transitives, car Brotli arrive souvent comme extra optionnel.

# Node.js: compare with 22.23.4 / 24.22.1 / 26.12.1
node -p process.version
# Python packages: run once per interpreter and virtual environment
python -m pip list 2>/dev/null | grep -iE '^(brotli|brotlicffi) '
pipdeptree -r -p brotli          # what pulled it in
# Debian / Ubuntu system library (then check the distro tracker)
dpkg-query -W -f='${db:Status-Abbrev} ${Package} ${Version}\n' 'libbrotli*'
# Container images, with an SBOM scanner such as Syft
syft <image> | grep -iE 'brotli|node|bun'

Trouver les runtimes embarqués

Les programmes compilés avec bun build --compile embarquent leur propre copie de Bun. L’avis de Bun indique qu’ils restent concernés tant qu’ils ne sont pas recompilés avec la version 1.4.4. Mettre à jour Bun sur la machine de build ne modifie pas les binaires déjà déployés.

# The Bun version inside a compiled executable
grep -aoE 'Bun v1\.[0-9]+\.[0-9]+' /path/to/binary | sort -u

Trouver les processus en cours

Contrôlez ce qui tourne, pas ce qui est installé. Sous Linux, un processus dont l’exécutable a été remplacé ou supprimé affiche (deleted). C’est un indice fort qu’il exécute encore une ancienne build.

# Processes still running a deleted (replaced) executable (run as root to see all users)
for p in /proc/[0-9]*; do
  e=$(readlink "$p/exe" 2>/dev/null) || continue
  case "$e" in *"(deleted)") echo "${p#/proc/} $e";; esac
done
# Processes older than the fix
pgrep -af 'node|bun|claude'      # then: ps -o pid,lstart,args -p <pid>

Sous macOS, lsof -p <pid> affiche l’exécutable chargé par un processus. Faites la correspondance par exécutable, pas par nom de processus, sinon vous manquerez les processus auxiliaires et les workers en arrière-plan.

Corriger, recompiler, redéployer

  • Passez aux versions corrigées indiquées dans le tableau. Utilisez le paquet corrigé de votre distribution lorsqu’un rétroportage existe.
  • Reconstruisez les images de conteneurs et les exécutables compilés, puis redéployez-les. Une image de base corrigée ne sert à rien pour une image que vous n’avez pas reconstruite.
  • Supprimez les runtimes inutilisés et les installations résiduelles, après avoir démontré qu’aucun service, job ou script n’en dépend. Prévoyez un retour arrière.
  • Lorsqu’aucun correctif n’existe encore, désactivez le décodage Brotli des entrées non fiables là où cela peut se faire sans risque. Pour Bun, fetch(url, { decompress: false }) fonctionne requête par requête. Attribuez à chaque mesure d’atténuation un responsable et une date de fin.

Redémarrer et vérifier

Après le redémarrage, vérifiez l’exécutable et la version de runtime effectifs du nouveau processus. Vérifiez ensuite le résultat métier : files d’attente vidées, tâches planifiées réussies, alertes de supervision bien reçues. Une commande de mise à jour réussie prouve seulement que la commande s’est exécutée.

6. Redémarrer sans risque les agents IA persistants

Les agents IA de longue durée conservent un état : appels d’outils en attente, approbations, messages en file d’attente et tâches qu’ils se sont attribuées. Les redémarrer tous en même temps peut faire perdre cet état. Voici la séquence qui a fonctionné pour nous :

  1. Inventoriez chaque session : version de l’exécutable, état (inactive, en attente ou occupée), dernière activité, travail en attente, approbations attendues ou détenues, événements non acquittés et mode de reprise.
  2. Sortez les approbations de la mémoire. Une approbation qui n’existe que dans la conversation d’une session est perdue au redémarrage. Avant de redémarrer, consignez chaque approbation en attente (ce qui a été approuvé, par qui, pour quelle modification exacte) dans un emplacement durable, comme le ticket ou la pull request. Ensuite, vérifiez que la session la voit toujours. En cas de doute, redemandez l’approbation plutôt que de la présumer.
  3. Validez d’abord la relève. Vérifiez que les sessions sur la build corrigée fonctionnent déjà et qu’un observateur d’événements indépendant des anciennes sessions est en cours d’exécution, afin qu’aucun événement ne dépende du redémarrage d’une session.
  4. Redémarrez d’abord les sessions inactives, une par une. Après chacune, vérifiez que l’ancien processus a disparu, que le nouveau exécute l’exécutable corrigé, que l’historique de conversation est intact et que toute connexion distante est rétablie. Passez ensuite à la suivante.
  5. Redémarrez en dernier le responsable le plus occupé, entre deux tâches. Faites-lui rédiger une note de passation, n’arrêtez son observateur qu’à zéro événement en attente et zéro événement non acquitté, redémarrez, puis vérifiez que l’observateur a repris à la position enregistrée sans rien manquer.
  6. Attendez-vous à des entrées en file d’attente à la reconnexion. L’une de nos sessions redémarrées a reçu un message qui attendait depuis dix jours et a agi en conséquence. Vérifiez la boîte de réception de chaque session avant de redémarrer, et prévenez son responsable.
  7. Arrêtez-vous et faites remonter si une étape ne permet pas de démontrer que le travail a été récupéré. Ne vous rabattez pas sur l’arrêt brutal de tout.

Définissez « terminé » avant de commencer. Terminé signifie : aucun processus en cours sur une build concernée, chaque exception documentée avec un responsable et une date de revérification, et les contrôles métier au vert. « Le paquet a été mis à jour » ne signifie pas terminé.

Sources