CVE-2026-90711 : usurpation d'adresse IP et rupture de cloisonnement réseau par analyse défaillante des plages IPv4 mappées IPv6 dans proxy-addr
HERMES THREAT SCORE & RAYON D'IMPACT SUR L'ÉCOSYSTÈME
Cible :proxy-addr (Node.js) & frameworks dépendants (Express req.ip, Koa, NestJS, Fastify) Tandis que le score CVSS v3.1 attribue à CVE-2026-90711 une note de 9.1 (Critique) centrée sur la confidentialité et l'intégrité, Hermes met en exergue l'immense rayon de souffle sur l'écosystème web mondial. proxy-addr totalise plus de 30 millions de téléchargements hebdomadaires sur npm en tant que brique fondamentale de résolution d'adresses d'Express. Lorsqu'une architecture de production définit des plages de confiance en notation IPv4 mappée IPv6, la bibliothèque assimile par inadvertance l'ensemble de l'Internet public (0.0.0.0/0) à un proxy amont légitime. Les attaquants contournent ainsi les listes blanches d'adresses IP, désactivent les limiteurs de débit, falsifient leur origine géographique et faussent les journaux d'audit de dizaines de milliers d'applications d'entreprise.
SCORE DE SÉVÉRITÉ AGENTIQUE HASS & RUPTURE DU FILTRAGE PÉRIMÉTRIQUE
Cible :Passerelles d'ingestion d'agents IA, récepteurs de webhooks & serveurs MCP Les agents d'intelligence artificielle modernes et les serveurs conformes au protocole Model Context Protocol (MCP) restreignent fréquemment leurs points d'entrée sensibles (webhooks d'exécution d'outils internes, synchronisation de mémoire vectorielle) aux adresses de bouclage (127.0.0.1) ou aux plages de Pods internes. En exploitant CVE-2026-90711, des attaquants distants forgent des adresses d'origine de confiance, déclenchant sans droit des actions agentiques privilégiées au travers des contrôles périmétriques.
CVE-2026-90711: Client IP Address Spoofing and Trust Boundary Bypass via IPv4-Mapped IPv6 CIDR Parsing Flaw in proxy-addrVULNÉRABILITÉ
Adversary embeds covert payload instructions into retrieved external data (web pages, repositories, emails) that subvert model planning when parsed by autonomous agents.
🔍 Pourquoi cette relation ? (Preuves & Provenance)
“CVE-2026-90711 weaponizes the agentic attack pattern formalized under AAP-002.”
- [technical_analysis]Pillar Security demonstrated that executing export BASH_ENV in Auto-Run causes bash to source hostile payloads upon subsequent commands. — Source : Pillar Security Research: Bypassing Cursor Auto-Run: When Shell Built-ins Lead to Host RCE (Fiabilité : HIGH)
1. Contexte technique & matrice d’impact
Section intitulée « 1. Contexte technique & matrice d’impact »Le module proxy-addr détermine l’adresse IP d’un client situé derrière des proxys inverses en analysant les en-têtes X-Forwarded-For de droite à gauche jusqu’à identifier une adresse non fiable.
| Paramètre | Spécification technique | Signification opérationnelle |
|---|---|---|
| Identifiant CVE | CVE-2026-90711 | NVD, GitHub Advisory GHSA-jqcg-44mw-7w3h |
| Classe de vulnérabilité | Contournement d’authentification par usurpation (CWE-290) | Calcul défaillant de masque de sous-réseau IPv4 mappé IPv6 |
| Composant vulnérable | proxy-addr (fonctions compile et parseNetmask) | Chaîne d’évaluation de confiance IP et d’analyse d’en-têtes |
| Mécanisme racine | Traitement erroné des préfixes < 96 sur les plages IPv4 mappées | Équivaut à 0.0.0.0/0, accordant la confiance à l’Internet entier |
| Vecteur d’attaque | Réseau (AV:N) via des en-têtes HTTP X-Forwarded-For forgés | Requêtes HTTP distantes non authentifiées |
| Privilèges requis | Aucun (PR:N) | Exploitation pré-authentification |
| Interaction utilisateur | Aucune (UI:N) | Aucune action de la victime requise |
| Versions affectées | >= 1.1.0, <= 2.0.7 | Déployé massivement au sein d’Express, Koa et NestJS |
| Version corrigée | 2.0.8 | Implémente la conversion de préfixe et la validation stricte de plage |
2. Anatomie de la vulnérabilité & analyse de cause racine
Section intitulée « 2. Anatomie de la vulnérabilité & analyse de cause racine »L’architecture des adresses IPv4 mappées en IPv6
Section intitulée « L’architecture des adresses IPv4 mappées en IPv6 »Conformément à la RFC 4291, les adresses IPv4 peuvent être encapsulées au sein de l’adressage IPv6 selon le format IPv4 mappé :
IPv4 10.0.0.1 -> IPv6 ::ffff:10.0.0.1 (::ffff:0a00:0001)
En notation binaire sur 128 bits :
- Bits 0 à 79 : tous à zéro (
0000...) - Bits 80 à 95 : tous à un (
1111..., correspondant àffff) - Bits 96 à 127 : l’adresse IPv4 sur 32 bits (
10.0.0.1)
Pour couvrir un sous-réseau IPv4 /8 dans l’espace IPv6, le préfixe doit obligatoirement englober les 96 premiers bits plus les 8 bits du masque IPv4 :
Longueur de préfixe IPv6 valide = 96 + 8 = 104 -> ::ffff:10.0.0.0/104
L’erreur de masque binaire dans proxy-addr
Section intitulée « L’erreur de masque binaire dans proxy-addr »Dans les versions vulnérables (<= 2.0.7), lorsqu’un administrateur déclarait une plage avec une notation abrégée comme ::ffff:10.0.0.0/8, la fonction d’analyse n’appliquait aucune translation :
// Analyse vulnérable de masque dans proxy-addr <= 2.0.7function parseNetmask(netmask) { if (netmask.indexOf('/') !== -1) { var parts = netmask.split('/'); var addr = ipaddr.parse(parts[0]); var range = parseInt(parts[1], 10);
// ERREUR : Si addr est une adresse IPv6 mappée (kind === 'ipv6'), // la valeur 'range' (8) est comparée directement aux 128 bits sans ajouter 96 ! return [addr, range]; }}Lorsqu’une requête client parvient à l’application :
- La connexion TCP de l’attaquant arrive avec une adresse IPv4, que le runtime Node.js représente sous la forme
::ffff:203.0.113.50. - L’algorithme de comparaison binaire teste les 8 premiers bits de
::ffff:203.0.113.50contre les 8 premiers bits de::ffff:10.0.0.0. - Étant donné que les deux adresses débutent par
0000:0000:..., leurs 8 premiers bits sont strictement identiques (0x00). - Le test d’appartenance renvoie invariablement
true!
En conséquence, absolument toutes les adresses IPv4 de la planète correspondent au masque /8. L’application en déduit que le socket client distant est un proxy inverse interne de confiance.
// Résolution de req.ip dans Express.jsfunction getClientIp(req, trust) { var addrs = allAddrs(req); // ex. [ "127.0.0.1", "203.0.113.50" ] for (var i = 0; i < addrs.length; i++) { // trust(203.0.113.50) renvoie TRUE en raison du bug CVE-2026-90711 ! if (!trust(addrs[i], i)) { return addrs[i]; } } return addrs[addrs.length - 1]; // L'adresse falsifiée par l'attaquant est retenue !}3. Vecteurs de menace, cinématique d’attaque & exploitation
Section intitulée « 3. Vecteurs de menace, cinématique d’attaque & exploitation »Schéma d’exécution de l’attaque
Section intitulée « Schéma d’exécution de l’attaque »flowchart TD A["Attaquant distant (IP : 203.0.113.50)"] -->|"1. Injecte l'en-tête : X-Forwarded-For: 127.0.0.1"| B["Serveur web Node.js / Express"] B -->|"Évalue socket.remoteAddress (::ffff:203.0.113.50)"| C["proxy-addr (test de confiance contre ::ffff:10.0.0.0/8)"] C -->|"Le préfixe /8 valide les 8 premiers zéros des deux adresses IPv6"| D["Anomalie : Renvoie TRUE (Le socket direct est considéré comme un proxy de confiance !)"] D -->|"Remonte la chaîne X-Forwarded-For vers la gauche"| E["Extrait l'adresse forgée par l'attaquant : 127.0.0.1"] E -->|"req.ip évalué à 127.0.0.1"| F["Middleware d'autorisation & de limitation de débit"] F -->|"2. Test : L'adresse req.ip est-elle dans [127.0.0.1, 10.0.0.0/8] ?"| G{"Accès accordé ?"} G -->|"OUI : Contournement du contrôle d'accès"| H["Console d'administration / Webhook d'agent IA"] H -->|"3. Exécution d'actions privilégiées et vol de données"| I["Compromission du serveur & de l'infrastructure interne"]Démonstration de l’exploitation
Section intitulée « Démonstration de l’exploitation »Un attaquant externe peut interroger une route réservée à l’hôte local en fournissant l’en-tête forgé :
# Point d'accès protégé par filtrage d'adresse IP :# app.get('/admin/debug/secrets', (req, res) => {# if (req.ip !== '127.0.0.1') return res.status(403).send('Interdit');# res.json(process.env);# });
# Commande d'exploitation :curl -H "X-Forwarded-For: 127.0.0.1" \ -H "Host: api.entreprise-victime.fr" \ https://api.entreprise-victime.fr/admin/debug/secretsL’application attribuant erronément la valeur 127.0.0.1 à req.ip, la condition de restriction d’accès est validée et les variables d’environnement du serveur sont divulguées.
4. Traces forensiques & tactiques post-exploitation
Section intitulée « 4. Traces forensiques & tactiques post-exploitation »Conséquences opérationnelles sur les architectures d’entreprise
Section intitulée « Conséquences opérationnelles sur les architectures d’entreprise »- Neutralisation des listes blanches d’adresses IP : les microservices exposant des routes techniques internes (
/metrics,/admin,/actuator, points d’entrée d’agents) protégées uniquement par filtrage d’IP deviennent directement accessibles depuis l’Internet. - Annihilation du rate limiting : les bibliothèques telles que
express-rate-limitregroupent les requêtes selonreq.ip. Un attaquant changeant d’adresseX-Forwarded-Forà chaque requête contourne toute limitation de débit lors d’attaques par force brute. - Falsification des journaux d’audit de sécurité : les systèmes SIEM ingérant les journaux HTTP enregistrent l’adresse IP falsifiée au lieu de l’adresse de l’attaquant, orientant les analystes vers de faux positifs internes.
- Contournement des restrictions géographiques : les services appliquant des règles territoriales strictes sont trompés par l’injection d’une adresse IP issue du pays autorisé.
Artefacts forensiques et indicateurs de détection
Section intitulée « Artefacts forensiques et indicateurs de détection »Lors d’un audit DFIR sur un incident potentiel :
- Divergence entre le socket réseau et l’en-tête applicatif : comparez l’adresse IP réelle de connexion (au niveau d’AWS ALB, Cloudflare ou HAProxy) avec le champ
req.ipconsigné par Node.js. - Exemples d’anomalies dans les journaux d’accès :
[ACCESS LOG] socket_ip=203.0.113.50 resolved_ip=127.0.0.1 uri="/admin/backup" status=200[ACCESS LOG] socket_ip=203.0.113.50 resolved_ip=10.244.0.1 uri="/api/v1/agent/execute" status=200
- Balayage rapide d’adresses privées successives : rafales de requêtes provenant d’une même adresse IP publique injectant des adresses RFC 1918 incrémentales (
10.0.0.1,10.0.0.2, etc.) pour contourner les quotas d’API.
5. Ingénierie de détection & règles Sigma
Section intitulée « 5. Ingénierie de détection & règles Sigma »Règle Sigma : détection d’injection d’adresses IP internes dans x-forwarded-for
Section intitulée « Règle Sigma : détection d’injection d’adresses IP internes dans x-forwarded-for »title: Usurpation d'adresse IP interne via X-Forwarded-For (CVE-2026-90711)id: c714a890-e832-4211-9a71-d10907110001status: experimentaldescription: Détecte les requêtes HTTP externes injectant des adresses de bouclage ou RFC1918 dans X-Forwarded-For pour atteindre des chemins sensibles.author: Hermes Codex Cyber Threat Intelligencedate: 2026-09-16references: - https://github.com/advisories/GHSA-jqcg-44mw-7w3h - https://nvd.nist.gov/vuln/detail/CVE-2026-90711logsource: category: webserver product: express / nginx / traefikdetection: selection_header: cs-method: - 'GET' - 'POST' - 'PUT' - 'DELETE' http_x_forwarded_for|startswith: - '127.' - '10.' - '192.168.' - '172.16.' - '172.17.' - '172.18.' - '172.19.' - '172.20.' - '172.21.' - '172.22.' - '172.23.' - '172.24.' - '172.25.' - '172.26.' - '172.27.' - '172.28.' - '172.29.' - '172.30.' - '172.31.' - '::1' selection_sensitive: cs-uri-stem|contains: - '/admin' - '/api/internal' - '/webhook' - '/actuator' - '/debug' filter_internal_source: c-ip|startswith: - '10.' - '172.16.' - '192.168.' - '127.' condition: selection_header and selection_sensitive and not filter_internal_sourcefields: - c-ip - http_x_forwarded_for - cs-uri-stem - sc-statusfalsepositives: - Proxys inverses d'infrastructure mal configurés ne nettoyant pas les en-têtes entrantslevel: hightags: - attack.defense_evasion - attack.t1562.001 - attack.initial_access - cve.2026.907116. Durcissement, remédiation & vérification
Section intitulée « 6. Durcissement, remédiation & vérification »-
Mettre à jour proxy-addr vers la version 2.0.8 OU supérieure : Mettez à jour la dépendance au sein de votre projet et vérifiez l’arbre des sous-dépendances :
Fenêtre de terminal npm install proxy-addr@^2.0.8npm update proxy-addrnpm ls proxy-addr -
Corriger les plages de confiance IPv4 mappées IPv6 : Si vos configurations Express définissent manuellement des sous-réseaux, utilisez la notation standard IPv4 ou appliquez le préfixe étendu sur 128 bits :
// CONFIGURATION VULNÉRABLE :app.set('trust proxy', '::ffff:10.0.0.0/8'); // Fait correspondre tout l'Internet !// CONFIGURATION SÉCURISÉE (Option A - Notation IPv4 standard) :app.set('trust proxy', '10.0.0.0/8');// CONFIGURATION SÉCURISÉE (Option B - Préfixe IPv6 explicite) :app.set('trust proxy', '::ffff:10.0.0.0/104'); -
Assainir les en-têtes sur les reverse proxys périmétriques (Nginx / HAProxy) : Assurez-vous que votre proxy de bordure réécrit intégralement l’en-tête
X-Forwarded-Forau lieu d’y concaténer la valeur fournie par le client externe :# Configuration Nginx sécuriséeproxy_set_header X-Forwarded-For $remote_addr; -
Auditer les dépendances Node.js en intégration continue : Exécutez un audit automatisé pour vérifier qu’aucun paquet transitif n’embarque une version vulnérable de
proxy-addr:Fenêtre de terminal npx audit-ci --high --package-manager npm