Brotli est un algorithme de compression conçu par Google, utilisé partout sur le web : la plupart des navigateurs l’acceptent, la plupart des serveurs web et réseaux de diffusion l’utilisent pour compresser les pages. Une faille dans son décodeur, CVE-2026-111497, publiée le 21 octobre 2026, permet une écriture hors des limites d’un tampon mémoire lors de la décompression de données construites pour cela. Ce billet explique pourquoi une faille dans une bibliothèque de compression est plus difficile à traiter qu’une faille dans une application, comment j’ai retrouvé toutes les copies de Brotli sur mes serveurs, et pourquoi, neuf jours après, une machine n’était toujours pas corrigée malgré des mises à jour automatiques actives.
Ce qu’est la faille
Un décodeur de compression lit un flux d’octets qui décrit, de façon très compacte, comment reconstituer les données d’origine : des symboles littéraux, des références vers des données déjà produites (« recopier 300 octets situés 4 000 octets plus haut »), des tailles de blocs. Le décodeur doit écrire le résultat dans un tampon de sortie, en vérifiant à chaque étape que ce qu’il s’apprête à écrire tient dans ce tampon. Une écriture hors limites (out-of-bounds write) se produit quand une de ces vérifications manque ou est fausse dans un cas particulier : un flux construit pour atteindre ce cas fait écrire le décodeur au-delà de la fin du tampon, dans la mémoire voisine. Selon ce qui s’y trouve, le résultat va du plantage à l’exécution de code.
Le point essentiel est qui fournit les données compressées. Le scénario dangereux est celui où un programme décompresse des données venues de l’extérieur : un client HTTP qui reçoit une réponse compressée d’un serveur malveillant, ou un serveur qui accepte des corps de requête compressés envoyés par un client. Compresser ses propres données ne pose aucun problème.
Le vrai problème : Brotli est partout, et en plusieurs exemplaires
Une faille dans nginx se corrige en mettant à jour nginx. Une faille dans une bibliothèque de compression se cache dans des dizaines de programmes, de deux façons différentes.
La bibliothèque partagée du système
rpm -q --whatrequires 'libbrotlidec.so.1()(64bit)'
freetype-2.13.2-8.el10.x86_64 # polices : PHP gd/imagick, génération d'images
exiv2-libs-0.28.3-5.el10.x86_64 # métadonnées d'images
libjxl-0.10.4-1.el10_1.x86_64 # images JPEG XL
libsoup3-3.6.5-5.el10.x86_64 # client HTTP de GNOME
libcurl-8.12.1-4.el10_2.7.x86_64 # curl, donc PHP curl, git, dnf…
dotnet-runtime-9.0-9.0.21-1.el10_2 # applications .NET
Le paquet libbrotli fournit une bibliothèque partagée que d’autres programmes chargent au démarrage. La requête rpm -q --whatrequires donne ceux qui en dépendent directement : curl (et donc l’extension curl de PHP, git, une partie des outils système), les bibliothèques de polices et d’images utilisées par PHP pour générer des vignettes, le moteur d’exécution .NET. Ce cas est le plus simple : on met à jour libbrotli, et tous ces programmes utilisent la version corrigée… à leur prochain démarrage.
Les copies embarquées
node -p "process.versions.brotli" # 1.2.0 (corrigé)
/opt/etherpad/nodejs/bin/node -p "process.versions.brotli" # 1.2.0 (corrigé)
/usr/share/wazuh-dashboard/node/bin/node -p "process.versions.brotli" # 1.0.9 (vulnérable, Node 18 en fin de vie)
Beaucoup de logiciels n’utilisent pas la bibliothèque du système mais embarquent leur propre copie, compilée à l’intérieur de leur exécutable. Node.js est l’exemple type : il contient sa propre version de Brotli, de zlib, d’OpenSSL. Et les applications qui embarquent elles-mêmes Node.js (un éditeur collaboratif installé dans /opt, le tableau de bord de Wazuh) ont leur copie de Node, donc leur copie de Brotli. Mettre à jour libbrotli ne change rien pour elles. Seule une nouvelle version du logiciel qui les embarque les corrige. Sur un de mes serveurs, le tableau de bord de Wazuh embarque Node 18, en fin de vie, avec Brotli 1.0.9 : aucune version corrigée de Node 18 n’existera, il faudra attendre que Wazuh mette à jour son Node intégré.
Ce que j’ai fait, dans l’ordre
- inventaire : les paquets qui dépendent de
libbrotli, puis tous les exécutables Node présents sur chaque machine (find / -xdev -type f -name node) avec leur version de Brotli intégrée (node -p process.versions.brotli) ; - les copies embarquées exposées en premier : l’éditeur collaboratif, accessible depuis Internet, est passé le jour même à une version de Node corrigée (24.22.1) ; les installations de Node du système sont passées au dépôt NodeSource, en version 26, qui embarque Brotli 1.2.0 corrigé ;
- une mesure d’attente côté serveur web, pour les cas où l’on décompresse ce qu’envoie le client ;
- la bibliothèque système, dès que le correctif de la distribution a été disponible ;
- les redémarrages de tout ce qui chargeait l’ancienne bibliothèque ;
- l’accès au tableau de bord Wazuh restreint aux seules adresses d’administration, en attendant une version corrigée.
La mesure d’attente
# snippets/server.d/reject-brotli-request.conf : refuser les CORPS de requête compressés en Brotli
if ($http_content_encoding ~* "br") {
return 415;
}
Un client HTTP peut annoncer, par l’en-tête Content-Encoding: br, que le corps de sa requête est compressé en Brotli. Aucun navigateur ne le fait spontanément, et aucune de mes applications n’en a besoin. Refuser ces requêtes par un code 415 (« type de média non supporté ») au niveau du serveur web empêche qu’un corps compressé malveillant atteigne une application qui le décompresserait. C’est une mesure étroite, sans effet de bord pour un usage normal, et facile à retirer.
Lire le journal des modifications, pas le numéro de version
rpm -q --changelog libbrotli | head -4
* Thu Oct 22 2026 RHEL Packaging Agent <…> - 1.1.0-8.1
- CVE-2026-111497 brotli: Out-of-Bounds Write in Google Brotli Decoder
- Resolves: RHEL-290261
Le projet Brotli a corrigé la faille dans ses versions 1.0.10, 1.1.1 et 1.2.1. La distribution, elle, a gardé la version 1.1.0 et rétroporté le correctif dans une nouvelle révision, 1.1.0-8.1. Un scanner qui compare « 1.1.0 » à la liste des versions vulnérables conclura à tort que le paquet est vulnérable. La seule vérification fiable est la présence de la CVE dans le journal des modifications du paquet installé.
Redémarrer ce qui utilise l’ancienne copie
# processus qui ont encore l'ANCIENNE bibliothèque en mémoire (fichier remplacé sur disque)
grep -l 'libbrotli.*(deleted)' /proc/[0-9]*/maps 2>/dev/null | cut -d/ -f3 \
| xargs -r -I{} sh -c 'echo "$(cat /proc/{}/comm) {}"' | sort -u
needs-restarting -s # la même chose, service par service
Quand un paquet remplace une bibliothèque, le fichier sur disque change, mais chaque processus déjà lancé garde en mémoire l’ancienne version, dont le fichier n’existe plus. Le noyau le signale dans /proc/<pid>/maps par la mention (deleted). La commande needs-restarting fait cette vérification pour vous, et donne la liste des services à redémarrer. Un lecteur multimédia, un serveur PHP, un service .NET peuvent tourner des semaines sans redémarrer : sans cette vérification, ils restent vulnérables bien après la mise à jour.
Le piège : une correction sans avis de sécurité
| Machine | Bibliothèque système | Corrigée ? | Pourquoi |
|---|---|---|---|
| serveur web principal | 1.1.0-8.el10_2.1 | oui | mise à jour manuelle le 22–23 octobre |
| laboratoire | 1.1.0-8.el10_2.1 | oui | idem |
| serveur de courrier | 1.1.0-8.el10_2.1 | oui | idem |
| serveur Wazuh | 1.1.0-8.el10_2 | non (au 30 octobre) | mises à jour automatiques en mode « sécurité » seulement, et aucun avis publié |
| Raspberry Pi (annuaire) | 1.0.9-10.el9_8 | non | mises à jour automatiques désactivées (mémoire insuffisante pour dnf) |
Neuf jours après la publication, deux machines n’étaient pas corrigées. Pour le Raspberry Pi, c’est un choix assumé : les mises à jour automatiques y sont désactivées parce que dnf y manque de mémoire, et il faut les faire à la main. Pour le serveur Wazuh, c’est plus instructif. Les mises à jour automatiques y sont actives, configurées pour n’installer que les mises à jour de sécurité (upgrade_type = security dans dnf-automatic). Or la distribution a publié le paquet corrigé sans avis de sécurité associé : pour dnf, c’est une mise à jour ordinaire. Le mode « sécurité seulement » ne l’a jamais installée, et mon script quotidien de surveillance des CVE, qui s’appuyait sur ces mêmes avis, ne l’a pas vue non plus.
Le même jour, un nouveau noyau corrigeant seize CVE est apparu, lui aussi sans avis. J’ai modifié mon script pour qu’il compare, pour chaque mise à jour en attente, les CVE citées dans son journal des modifications à celles du paquet installé : il détecte désormais ces correctifs « silencieux ». Et la question du mode des mises à jour automatiques (sécurité seulement, ou toutes les mises à jour) se pose à nouveau : le mode « sécurité » ne vaut que ce que valent les avis publiés.
En résumé
- une faille dans une bibliothèque se cache dans tous les programmes qui la chargent, et dans tous ceux qui en embarquent une copie ;
- l’inventaire se fait en deux temps : dépendances du paquet système, puis copies embarquées (Node.js, Python, Java, Go…) ;
- une mise à jour de bibliothèque n’agit qu’au redémarrage des processus :
needs-restarting; - le journal des modifications fait foi, pas le numéro de version ;
- les mises à jour automatiques « de sécurité » ne voient que ce qui est publié comme tel. Vérifiez les mises à jour en attente qui citent des CVE.
Ce billet fait partie d’une série de tutoriels d’administration système (tag « pas à pas »).
Série « Pas à pas », tutoriel 2/56. · ← Tutoriel précédent · Tutoriel suivant → (à paraître le 31/10) · English version














