Faille Node.js : correctif Node2Shell et vérification de la version de Node.js (WordPress)

Mention d'affiliation v3.1
Mention d'affiliation v3.2 (mobile)
Environnement de test Famille RHELAlmaLinux 9.8Nginx + Apache · WordPress 7.1.3Vérifié le 2026-10-23

Node2Shell est le surnom de CVE-2026-111497, une faille de Node.js dont le correctif a été publié le 21 octobre 2026 ; son titre officiel décrit une écriture hors limites dans le décodeur Brotli de Google. Comme la décompression de données Brotli forgées peut mener à une exécution de code à distance, la première chose à faire est de vérifier les versions de Node.js, Bun et libbrotli de votre serveur et de les mettre à jour vers les versions corrigées.

Sur un serveur Nginx + Apache sous WordPress 7.1.3, j'ai vérifié quels paquets utilisent le décodeur Brotli et par quel chemin les requêtes distantes de WordPress passent par libbrotli.

1️⃣ Node2Shell, la faille Node.js : comment fonctionne le défaut et qui est concerné

Le nom Node2Shell est un surnom donné par l'équipe de recherche de la société de sécurité Imperva dans un article du 22 octobre. Le titre de l'avis de nodejs.org est [Brotli decoder out-of-bounds write permits remote code execution]. Le défaut se trouve dans le code source du décodeur Brotli de Google intégré à Node.js (c/dec/decode.c) : modifier le code de votre application ne suffit donc pas à le bloquer.

D'après la fiche NVD, le décodeur de Google Brotli 1.0.3 à 1.2.0 écrit au-delà de la fin d'un tampon lorsqu'il décompresse un flux forgé, ce qui corrompt la mémoire d'état adjacente du décodeur. Avec ce défaut, un attaquant distant non authentifié peut lire le contenu de la mémoire du serveur, interrompre le service et exécuter du code arbitraire.

Il existe deux évaluations de gravité. L'avis de nodejs.org la classe High, et Google, qui a émis la CVE, lui attribue 9,2 (Critical) selon CVSS 4.0. Au 23 octobre, la NVD n'a pas encore terminé sa propre analyse, il n'y a donc pas encore de score NVD. Les types de faiblesse sont une valeur de retour non vérifiée (CWE-252) et une écriture hors limites (CWE-787).

D'après l'avis de sécurité de nodejs.org, toutes les branches prises en charge de Node.js (22.x, 24.x et 26.x) sont concernées, et les versions corrigées sont 22.23.4, 24.22.1 et 26.12.1. Le même avis précise que les branches en fin de vie sont toujours considérées comme concernées à chaque avis de sécurité.

Tout programme qui utilise le même décodeur est concerné, même en dehors de Node.js. Le 21 octobre, Google a publié trois versions de la bibliothèque Brotli qui corrigent la même CVE : 1.0.10, 1.1.1 et 1.2.1. Bun a inscrit Brotli dans la liste des correctifs de sécurité de l'annonce de la 1.4.4 le 22 octobre, et une issue de son dépôt précise que cette version contient le correctif qui met à jour brotli.

destinationVersions concernéesVersion corrigéeSource
Node.jsToutes les branches prises en charge 22.x · 24.x · 26.x · branches en fin de vie22.23.4 · 24.22.1 · 26.12.1Avis de sécurité de nodejs.org (2026-10-21)
Bibliothèque Brotli de Google1.0.3 ~ 1.2.01.0.10 · 1.1.1 · 1.2.1Fiche NVD · versions de google/brotli (2026-10-21)
BunBrotli dans la liste des correctifs de sécurité de l'annonce de la 1.4.41.4.4Annonce de Bun (2026-10-22)
Paquet libbrotli de la distributionPaquets construits à partir de Brotli 1.0.3 à 1.2.0La version indiquée par l'avis de sécurité de la distribution (aucune au 2026-10-23)Page du suivi de sécurité de chaque distribution

Node.js ne décompresse pas de lui-même les données Brotli qu'il reçoit. D'après l'avis de sécurité de l'hébergeur Vercel, les points exposés sont ceux où une application ou une bibliothèque décompresse des données compressées venues de l'extérieur. C'est le cas quand le fetch() intégré reçoit une réponse br et quand Express 5 ou body-parser 2 décompresse un corps de requête br ; le serveur HTTP par défaut, qui se contente de recevoir, ne décompresse rien.

Au 23 octobre, aucune exploitation réelle n'a été confirmée. Cette CVE ne figure pas dans le catalogue des vulnérabilités exploitées connues de la CISA (KEV, version du 21 octobre). Le niveau d'exploitation que la CISA attribue dans la fiche CVE est passé le 23 octobre à [preuve de concept (PoC) publique].

C'est l'emplacement du décodeur qui détermine ce qu'il faut faire. Les binaires officiels de Node.js intègrent le décodeur dans l'exécutable, mais certaines versions compilées, comme le paquet nodejs de Debian, utilisent la libbrotli du système. Les distributions Linux installent le même code source sous forme de paquet libbrotli, partagé par des programmes comme curl.

2️⃣ Les sites WordPress et PHP aussi concernés : le chemin par lequel les requêtes distantes décompressent les réponses avec libbrotli

Même un serveur WordPress sans Node.js contient le même décodeur, installé sous forme de paquet libbrotli. Au 23 octobre, WordPress, curl et PHP n'ont fait aucune déclaration officielle sur cette CVE, et elle ne figure pas non plus dans la liste officielle des vulnérabilités de curl. Cette section ne fait donc que confirmer la structure, à partir du code du cœur, de la documentation de curl et de mesures sur un serveur de test.

Sur le serveur Nginx + Apache mesuré, cinq paquets exigeaient la bibliothèque du décodeur (libbrotlidec), et celui qui est relié à WordPress est libcurl.

rpm -q --whatrequires 'libbrotlidec.so.1()(64bit)' | sed 's/-[0-9].*//'
freetype
brotli-devel
libjxl
libcurl
libsoup

libcurl exige libbrotli pour décompresser les réponses HTTP compressées. D'après la documentation officielle de curl, une libcurl compilée avec la bibliothèque brotli décompresse automatiquement les réponses dont le Content-Encoding est br. L'extension curl de PHP appelle directement cette libcurl.

ldd /usr/lib64/libcurl.so.4 | grep brotli
    libbrotlidec.so.1 => /lib64/libbrotlidec.so.1 (0x00007f32803ed000)
    libbrotlicommon.so.1 => /lib64/libbrotlicommon.so.1 (0x00007f327f858000)
php -i | grep BROTLI
BROTLI => Yes

La version de libcurl utilisée par WordPress s'affiche à la ligne [Version de cURL] lorsque vous dépliez la section [Serveur] dans l'onglet [Informations] de [Outils › Santé du site] dans l'administration. Cette ligne n'indique pas si brotli est pris en charge : vérifiez-le avec des commandes serveur comme ci-dessus.

Écran Santé du site, onglet Informations : menu de gauche Outils › Santé du site et lignes Architecture serveur, Serveur web, Version de PHP, PHP SAPI et Version de cURL de la section Serveur — vérification, à la ligne Version de cURL, de la version de libcurl, le chemin côté WordPress de la faille Node.js Node2Shell (WordPress 7.1.3)

La ligne [Version de cURL] de l'image affiche la version de libcurl avec la bibliothèque SSL avec laquelle elle a été compilée. D'après la documentation de curl, br est pris en charge depuis la 7.57.0 : si cette valeur est inférieure, libcurl ne peut pas décompresser les réponses br. Si elle est égale ou supérieure, vérifiez avec des commandes serveur si brotli a été inclus dans la compilation.

Lorsque la bibliothèque Requests du cœur de WordPress envoie une requête vers une adresse externe, elle définit la valeur [CURLOPT_ENCODING] du handle curl sur une chaîne vide. D'après la documentation de curl, une chaîne vide signifie accepter toutes les méthodes de compression prises en charge par cette libcurl. Si le serveur distant envoie une réponse compressée en br, c'est donc la libbrotli du serveur WordPress qui la décompresse.

grep -n CURLOPT_ENCODING wp-includes/Requests/src/Transport/Curl.php
110:            curl_setopt($this->handle, CURLOPT_ENCODING, '');
212:            curl_setopt($this->handle, CURLOPT_ENCODING, 'none');

La ligne 110 est la valeur par défaut de toutes les requêtes, et la ligne 212 ne s'exécute que lorsqu'une requête dont la décompression a échoué est renvoyée sans compression. Il n'y a aucun moyen de l'atteindre directement depuis l'extérieur : il faudrait d'abord prendre le contrôle d'un serveur dont WordPress récupère les réponses, par exemple pour la vérification des mises à jour du cœur et des extensions, oEmbed ou les flux RSS.

🧩 Déroulement de l'attaque Node2Shell
Schéma explicatif
①Données compressées venues de l'extérieurUn corps de requête compressé en br ou la réponse d'un serveur distant
②L'application les décompresse avec le décodeur BrotliNode.js fetch() · Express 5 · Bun fetch() · WordPress via libcurl → libbrotli
③Défaut du décodeur BrotliUn flux forgé écrit au-delà de la fin du tamponCVE-2026-111497
④Exécution de code sur le serveurFuite de mémoire · interruption du service · prise de contrôle du serveur
✅ Les versions corrigées bloquent ce déroulementNode.js 22.23.4 · 24.22.1 · 26.12.1Bun 1.4.4Brotli 1.0.10 · 1.1.1 · 1.2.1

Le déroulement commence par des données compressées en br venues de l'extérieur. Le défaut se déclenche à l'étape où le fetch() de Node.js, Express 5, le fetch() de Bun ou la libbrotli appelée par la libcurl de WordPress les décompresse. Le résultat mène à une exécution de code sur le serveur. Les versions corrigées bloquent ce déroulement : ce qu'il faut faire, c'est remplacer le décodeur par une version corrigée.

Le mod_brotli d'Apache ne lie que la bibliothèque de l'encodeur (libbrotlienc), qui compresse les réponses : une configuration qui active la compression avec ce module ne se trouve donc pas sur le chemin du défaut du décodeur.

ldd /usr/lib64/httpd/modules/mod_brotli.so | grep brotli
    libbrotlienc.so.1 => /lib64/libbrotlienc.so.1 (0x00007f34b4b79000)
    libbrotlicommon.so.1 => /lib64/libbrotlicommon.so.1 (0x00007f34b4a79000)

3️⃣ Vérifier la version de Node.js : Node.js, Bun et libbrotli sont-ils sur le serveur, et en quelle version

Commencez par faire l'inventaire de ce qui est installé sur le serveur. Pour Node.js, regardez la version avec [node -v] : si la valeur est inférieure à 22.23.4, 24.22.1 ou 26.12.1 selon sa branche, elle est concernée.

Votre environnementCommande de vérificationCe qui est corrigé
Installeur nodejs.org · nvm · image Docker officielle[node -v]Node.js lui-même (22.23.4 · 24.22.1 · 26.12.1)
nodejs de la distribution · ligne libbrotli dans ldd[ldd $(command -v node)]Paquet libbrotli
nodejs de la distribution · résultat ldd 0[ldd $(command -v node)]Paquet nodejs (après l'avis de sécurité de la distribution)
Node.js Selector en hébergement mutualisé (CloudLinux alt-nodejs)Annonce de l'hébergeur · numéro de release du paquetPaquet déployé par l'hébergeur + redémarrage de l'application
Bun[bun --version]Bun 1.4.4 ou ultérieur
Pas de Node.js (WordPress seul)Vérifier libcurl avec [ldd]Paquet libbrotli + redémarrage de php-fpm · d'ici là, mesure temporaire
Hébergement infogéréAnnonce de l'hébergeur · demande au supportPaquets mis à jour par l'hébergeur

Sur un PC (Windows ou macOS), réinstallez avec l'installeur de la version corrigée de nodejs.org ; avec nvm, regardez la version avec [nvm ls] puis réinstallez par numéro de branche, comme [nvm install 24]. Les tags 22, 24 et 26 de l'image Docker officielle node ont été reconstruits avec les versions corrigées le 22 octobre : récupérez à nouveau l'image, reconstruisez et déployez. L'avis de Vercel recommande aussi de passer aux versions corrigées les images de conteneur que vous construisez vous-même, les environnements de CI et les environnements de développement locaux.

Sur un serveur où vous avez installé Node.js via un stream de module dnf, [node -v] affiche une version de la branche de ce stream ; même si la branche est la même, il faut mettre à jour si les derniers chiffres n'atteignent pas la version corrigée.

node -v
v24.xx.x

Pour savoir si Node.js utilise la libbrotli du système ou son décodeur intégré, vérifiez avec [ldd]. Si la sortie contient une ligne libbrotlidec, la version compilée utilise la bibliothèque partagée ; si elle affiche 0, comme ci-dessous, le décodeur est intégré et seule la mise à jour de Node.js lui-même corrige le problème.

ldd $(command -v node) | grep -c brotli
0

Pour Bun, vérifiez la version avec [bun --version] et mettez à jour si elle est inférieure à 1.4.4.

Sur les systèmes RPM, regardez la version du paquet libbrotli avec [rpm -q libbrotli]. Les numéros de version des paquets sont attribués par la distribution : ne les comparez pas directement aux numéros de version de Google, mais à la version publiée par l'avis de sécurité de la distribution pour ce numéro de CVE.

rpm -q libbrotli
libbrotli-1.0.x-x.el9_x.x86_64

Pour savoir si la distribution a publié un avis, utilisez [dnf updateinfo info --cve CVE-2026-111497]. Si la sortie est vide, ce dépôt n'a pas encore d'avis pour cette CVE.

dnf updateinfo info --cve CVE-2026-111497

Un paquet qui reprend seulement le correctif sans changer le numéro de version garde le numéro de CVE dans son journal des modifications. On vérifie donc avec [rpm -q --changelog libbrotli | grep CVE-2026-111497] : si une ligne s'affiche, le paquet est corrigé.

rpm -q --changelog libbrotli | grep CVE-2026-111497

Au 23 octobre, les paquets libbrotli de Debian, Ubuntu, Red Hat et AlmaLinux n'ont pas encore de version corrigée pour cette CVE. Alpine n'a de version corrigée que pour son paquet nodejs ; la source est la page du suivi de sécurité de chaque distribution. Pour l'instant, la marche à suivre est donc de surveiller la publication de l'avis de sécurité de la distribution et de mettre à jour dès qu'il paraît.

Sous Debian et Ubuntu, le paquet s'appelle libbrotli1, et j'ai vérifié les mêmes points sur un serveur de test Debian 12 d'un hébergeur infogéré. Sur ce serveur aussi, libcurl liait libbrotlidec et l'extension curl de PHP prenait en charge brotli. Le journal des modifications se consulte avec [zcat /usr/share/doc/libbrotli1/changelog.Debian.gz | grep CVE-2026-111497].

dpkg -l libbrotli1 | tail -1
ii  libbrotli1:amd64 1.1.0-2+0~20240305.8+debian12~1.gbp62df71 amd64        library implementing brotli encoder and decoder (shared libraries)
ldd /usr/lib/x86_64-linux-gnu/libcurl.so.4 | grep brotli
    libbrotlidec.so.1 => /lib/x86_64-linux-gnu/libbrotlidec.so.1 (0x00007fa2baac6000)
    libbrotlicommon.so.1 => /lib/x86_64-linux-gnu/libbrotlicommon.so.1 (0x00007fa2b9cb9000)

4️⃣ Mettre à jour Node.js : passer à la version corrigée et vérifier à nouveau

Chez les hébergeurs infogérés et mutualisés, c'est l'hébergeur qui met à jour les paquets du serveur : vérifiez via ses annonces ou son support si Node.js et libbrotli ont été corrigés. Si vous gérez vous-même le serveur, passez Node.js à la version corrigée de l'avis de nodejs.org, et Bun à la 1.4.4 ou ultérieure avec [bun upgrade].

Le Node.js installé depuis un stream de module de la distribution se met à jour avec une commande du type [dnf upgrade nodejs] une fois la version corrigée arrivée dans ce dépôt. Sur un serveur où Node.js a été téléchargé et installé directement depuis nodejs.org, remplacez-le par la version corrigée au même endroit. Les branches en fin de vie ne reçoivent pas de version corrigée : passez à une branche prise en charge, 22, 24 ou 26.

Le Node.js Selector des hébergements mutualisés (CloudLinux alt-nodejs) embarque sa propre copie de brotli : mettre à jour la libbrotli du système ne le corrige pas. D'après l'annonce de CloudLinux, le nouveau binaire ne s'exécute qu'une fois le paquet déployé par l'hébergeur en place et l'application redémarrée. La même annonce précise que pour les versions en fin de vie, la valeur de [node --version] ne change pas : on vérifie alors le numéro de release du paquet.

Pour le paquet libbrotli, dès que l'avis de sécurité de la distribution paraît, mettez ce paquet à jour et redémarrez les processus qui ont la bibliothèque chargée en mémoire pour qu'ils passent à la nouvelle bibliothèque. Les bibliothèques partagées sont lues au démarrage d'un processus : sur un serveur WordPress, le redémarrage de php-fpm fait partie de la même étape.

Après la mise à jour, vérifiez que [node -v] affiche la version corrigée ou ultérieure et que [rpm -q libbrotli] affiche la version de l'avis de la distribution. Vérifiez la présence du numéro de CVE dans le journal des modifications avec [rpm -q --changelog libbrotli | grep CVE-2026-111497]. Si vous avez redémarré php-fpm, contrôlez aussi l'état du service et la réponse du site.

systemctl is-active php-fpm
active
curl -s -o /dev/null -w '%{http_code}\n' 'https://example.com/?r=1'
200

5️⃣ Mesure temporaire avant le correctif : désactiver la décompression br dans les requêtes distantes de WordPress

La page CVE de Red Hat recommande de configurer les applications pour qu'elles n'acceptent pas, ou ne décompressent pas automatiquement, la compression br provenant de sources non fiables.

Même avant la sortie des paquets corrigés, vous pouvez bloquer dès maintenant le chemin côté WordPress. WordPress fournit le hook [http_api_curl], qui transmet le handle curl à chaque requête distante. Définir [CURLOPT_ENCODING] sur null à cet endroit désactive la décompression automatique de libcurl.

Placer le fichier ci-dessous dans le dossier [wp-content/mu-plugins/] l'applique à toutes les requêtes distantes ; le nom du fichier n'est qu'un exemple. Pour revenir en arrière, supprimez ce fichier.

<?php
// no-brotli-decode.php — WordPress distant dans les requêtes libcurl Auto décompression Désactivé(libbrotli correctif version publiée jusqu'à)
if ( ! defined( 'ABSPATH' ) ) {
    exit;
}
add_action( 'http_api_curl', function ( $handle ) {
    curl_setopt( $handle, CURLOPT_ENCODING, null );
}, 99 );

On pourrait croire qu'en limitant [CURLOPT_ENCODING] à [gzip, deflate], br disparaît. Cette valeur ne fait que retirer br de l'en-tête Accept-Encoding de la requête : si le serveur distant répond en br, libcurl décompresse quand même ce qu'il reçoit. Sur le site de test, en récupérant httpbin.org/brotli avec cette même valeur, la réponse au Content-Encoding br a été décompressée et lisible telle quelle.

D'après la documentation de curl, la seule valeur qui désactive la décompression automatique est null : avec null, l'en-tête Accept-Encoding n'est pas envoyé et le corps reçu n'est pas décompressé non plus. Les réponses gzip et deflate sont décompressées directement par la bibliothèque Requests de WordPress et restent lisibles ; seules les réponses br restent non décompressées.

Ce hook est celui que WordPress a branché sur le point [curl.before_send], que Requests appelle juste avant d'exécuter curl. Il est appelé à la ligne 174 de Curl.php et curl s'exécute à la ligne 205 : la chaîne vide que le cœur a placée à la ligne 110 est donc écrasée entre les deux.

Vérifiez l'application avec le fichier de test ci-dessous. httpbin.org est une adresse de test qui renvoie les en-têtes de la requête reçue ainsi que des réponses compressées en br et en gzip : on voit d'un coup ce que WordPress envoie et ce qu'il décompresse.

<?php
// br-check.php — WordPress distant requête quelle compression envoie si elle décompresse Vérifier(httpbin.org test Adresse)
$h = json_decode( wp_remote_retrieve_body( wp_remote_get( 'https://httpbin.org/headers' ) ), true );
echo 'Accept-Encoding: ', isset( $h['headers']['Accept-Encoding'] ) ? $h['headers']['Accept-Encoding'] : '(none)', PHP_EOL;
foreach ( array( 'brotli', 'gzip' ) as $p ) {
    $r = wp_remote_get( 'https://httpbin.org/' . $p );
    $readable = strpos( wp_remote_retrieve_body( $r ), ': true' ) !== false;
    echo $p, ': Content-Encoding=', wp_remote_retrieve_header( $r, 'content-encoding' ), ' / ', strlen( wp_remote_retrieve_body( $r ) ), ' bytes / ', ( $readable ? 'readable' : 'not decoded' ), PHP_EOL;
}

Avant d'ajouter le fichier, Accept-Encoding contient br et la réponse br est décompressée et lisible.

wp eval-file br-check.php
Accept-Encoding: deflate, gzip, br
brotli: Content-Encoding=br / 316 bytes / readable
gzip: Content-Encoding=gzip / 317 bytes / readable

Après l'ajout du fichier, l'en-tête Accept-Encoding disparaît, la réponse br reste non décompressée et la réponse gzip reste lisible.

wp eval-file br-check.php
Accept-Encoding: (none)
brotli: Content-Encoding=br / 199 bytes / not decoded
gzip: Content-Encoding=gzip / 272 bytes / readable

Une fois le paquet libbrotli corrigé de la distribution installé et php-fpm redémarré, supprimez ce fichier pour revenir à l'état d'origine.

🔢 FAQ & contenus recommandés

Les mises à jour automatiques de WordPress ne concernent que le cœur, les extensions, les thèmes et les traductions ; libbrotli et Node.js sont des paquets installés sur le serveur, mis à jour par l'administrateur du serveur ou l'hébergeur. Ce n'est qu'après leur mise à jour et le redémarrage de php-fpm que les requêtes distantes de WordPress sont traitées avec la nouvelle bibliothèque.

Qu'une extension passe par la WordPress HTTP API ou appelle directement les fonctions curl, elle passe par la même libcurl. Ce qu'il faut corriger, c'est le paquet libbrotli du serveur ; en attendant la version corrigée, vous pouvez désactiver la décompression automatique des requêtes distantes de WordPress avec le réglage de la partie 5️⃣. D'après la documentation de curl, un code qui appelle directement les fonctions curl sans définir [CURLOPT_ENCODING] ne décompresse pas automatiquement les réponses compressées. Un code qui les appelle directement en la définissant sur une chaîne vide se trouve hors du hook [http_api_curl] : vérifiez séparément les requêtes de cette extension.

Les fiches CVE-2026-111497 de la NVD et de cve.org (CNA Google) indiquent l'emplacement du défaut (c/dec/decode.c), les versions concernées et le type de faiblesse. Les notes des versions v1.2.1, v1.1.1 et v1.0.10 de google/brotli consignent la correction. Les détails permettant de reproduire l'attaque n'ont pas été inclus dans cet article.

ℹ️ Mention d'affiliation
Le contenu de ce site contient des liens d'affiliation. Lorsqu'un visiteur achète un produit ou un service via l'un de ces liens, le site perçoit une commission du vendeur. Le montant payé par l'acheteur(il baisse lors des promotions ↓), lui, n'augmente pas. Les prix, remises et stocks indiqués correspondent à la date de rédaction et peuvent différer ; vérifiez-les donc auprès du vendeur avant d'acheter. Les produits sont choisis et évalués selon nos propres critères, et les commissions n'influencent ni l'ordre de présentation ni le contenu des évaluations.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Prouvez que vous êtes humain : 9   +   6   =