Aller au contenu

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

HERMES THREAT SCORE & RAYON D'IMPACT SUR L'ÉCOSYSTÈME

Cible : proxy-addr (Node.js) & frameworks dépendants (Express req.ip, Koa, NestJS, Fastify)
Confiance : 96%
91 / 100
CRITIQUE

Mesure la pertinence opérationnelle réelle, la militarisation de l'exploit et la posture de menace active.

Décomposition des dimensions
Exploitabilité 19 / 20
Activité de menace 16 / 20
Militarisation 18 / 20
Exposition 19 / 20
Prévalence 20 / 20
Impact 18 / 20
Maturité de l'exploit 18 / 20
Potentiel d'attaque en chaîne 19 / 20
⚖️ Divergence & Justification opérationnelle

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.

HASS

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
Confiance : 92%
78 / 100
ÉLEVÉ

Mesure le risque systémique spécifique découlant de l'autonomie, de l'autorité des outils et de l'exécution en cascade.

Décomposition des dimensions
Autonomie décisionnelle 14 / 20
Accès aux outils & APIs 16 / 20
Privilege 14 / 15
Persistence 10 / 15
External Impact 12 / 15
Propagation 12 / 15
⚖️ Divergence & Justification opérationnelle

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.

🕸️ Graphe de connaissances connecté & Provenance

CVE-2026-90711: Client IP Address Spoofing and Trust Boundary Bypass via IPv4-Mapped IPv6 CIDR Parsing Flaw in proxy-addrVULNÉRABILITÉ

Nœuds connectés : 1
Relations sortantes actives
→ exploitsMODÈLE D'ATTAQUEAAP-002: Indirect Context Injection
92% VERY_HIGH

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.”

Preuves vérifiées associées :

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ètreSpécification techniqueSignification opérationnelle
Identifiant CVECVE-2026-90711NVD, 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érableproxy-addr (fonctions compile et parseNetmask)Chaîne d’évaluation de confiance IP et d’analyse d’en-têtes
Mécanisme racineTraitement 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’attaqueRéseau (AV:N) via des en-têtes HTTP X-Forwarded-For forgésRequêtes HTTP distantes non authentifiées
Privilèges requisAucun (PR:N)Exploitation pré-authentification
Interaction utilisateurAucune (UI:N)Aucune action de la victime requise
Versions affectées>= 1.1.0, <= 2.0.7Déployé massivement au sein d’Express, Koa et NestJS
Version corrigée2.0.8Implé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

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.7
function 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 :

  1. 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.
  2. L’algorithme de comparaison binaire teste les 8 premiers bits de ::ffff:203.0.113.50 contre les 8 premiers bits de ::ffff:10.0.0.0.
  3. Étant donné que les deux adresses débutent par 0000:0000:..., leurs 8 premiers bits sont strictement identiques (0x00).
  4. 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.js
function 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 »
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"]

Un attaquant externe peut interroger une route réservée à l’hôte local en fournissant l’en-tête forgé :

Fenêtre de terminal
# 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/secrets

L’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 »
  1. 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.
  2. Annihilation du rate limiting : les bibliothèques telles que express-rate-limit regroupent les requêtes selon req.ip. Un attaquant changeant d’adresse X-Forwarded-For à chaque requête contourne toute limitation de débit lors d’attaques par force brute.
  3. 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.
  4. 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.ip consigné 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.

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-d10907110001
status: experimental
description: 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 Intelligence
date: 2026-09-16
references:
- https://github.com/advisories/GHSA-jqcg-44mw-7w3h
- https://nvd.nist.gov/vuln/detail/CVE-2026-90711
logsource:
category: webserver
product: express / nginx / traefik
detection:
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_source
fields:
- c-ip
- http_x_forwarded_for
- cs-uri-stem
- sc-status
falsepositives:
- Proxys inverses d'infrastructure mal configurés ne nettoyant pas les en-têtes entrants
level: high
tags:
- attack.defense_evasion
- attack.t1562.001
- attack.initial_access
- cve.2026.90711

  1. 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.8
    npm update proxy-addr
    npm ls proxy-addr
  2. 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');
  3. 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-For au lieu d’y concaténer la valeur fournie par le client externe :

    # Configuration Nginx sécurisée
    proxy_set_header X-Forwarded-For $remote_addr;
  4. 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