Rédigé par : Rachid.A
Traduction : Yewlne
01 texte traduit
Récemment, j’ai décidé de collaborer avec Yasser Allam, connu sous le pseudonyme inzo_. Après avoir discuté de plusieurs objectifs potentiels, nous avons décidé de nous concentrer sur Next.js (qui a 130 000 étoiles sur GitHub et plus de 9,4 millions de téléchargements par semaine). C’est un cadre que je connais très bien et avec lequel j’ai de belles expériences créatives, comme le prouvent mes recherches précédentes. Par conséquent, le « nous » dans cet article fait naturellement référence à nous deux.
Next.js est un framework JavaScript complet basé sur React, doté de nombreuses fonctionnalités — c’est un endroit idéal pour explorer les détails. Avec foi, curiosité et résilience, nous entreprenons un voyage pour explorer ces recoins peu connus et découvrir les trésors qui s’y cachent.
Peu de temps après, nous avons découvert un problème majeur dans le middleware. Son impact est vaste, toutes les versions sont affectées, et il est possible d’exploiter cette vulnérabilité sans aucune condition préalable - nous allons bientôt le montrer en détail.
Table des matières
Middleware de Next.js
Outil d’autorisation : code ancien de niveau trésor
Ordre d’exécution avec middlewareInfo.name
Outil autorisé : Hier est devenu un poème, aujourd’hui en vaut encore plus la peine.
/src répertoire
Profondeur de récursion maximale
Exploitation de vulnérabilités
Contourner l’autorisation/écrire à nouveau
Contourner le CSP
Réaliser un DoS par empoisonnement de cache (Quoi ? )
Clarifier
Annonce de sécurité - CVE-2025-29927
Avertissement
Conclusion
Middleware Next.js
Le middleware vous permet d’exécuter du code avant que la requête ne soit terminée. Ensuite, vous pouvez modifier le contenu de la réponse en fonction de la requête entrante, en réécrivant, redirigeant, modifiant les en-têtes de requête ou de réponse, ou en renvoyant directement la réponse (extrait de la documentation de Next.js).
En tant que cadre complet, Next.js possède son propre middleware — c’est une caractéristique importante et largement utilisée. Ses cas d’utilisation sont nombreux, parmi lesquels les plus importants incluent :
Réécriture de chemin (Path rewriting)
Redirections côté serveur
Ajouter des en-têtes de réponse (comme CSP, etc.)
Le plus important est : l’authentification (Authentication) et l’autorisation (Authorization)
Une utilisation courante des middleware est l’autorisation, qui implique de protéger des chemins spécifiques en fonction de conditions particulières.
Vérification et autorisation : assurez-vous de l’identité de l’utilisateur et vérifiez le cookie de session avant d’accorder l’accès à des pages ou des routes API spécifiques (documentation Next.js).
Exemple : Lorsque l’utilisateur essaie d’accéder à /dashboard/admin, la requête passe d’abord par un middleware, qui vérifie si le cookie de session de l’utilisateur est valide et s’il dispose des autorisations nécessaires. Si la validation réussit, le middleware transmet la requête ; sinon, le middleware redirige l’utilisateur vers la page de connexion :
Outil d’autorisation : code ancien de niveau trésor
Comme un grand homme l’a dit un jour, « talk is cheap, show me the bug », évitons les discours inutiles et allons droit au but ; en parcourant la version ancienne du framework (v12.0.7), nous avons découvert ce code :
Lorsqu’une application Next.js utilise un intergiciel, la fonction runMiddleware est appelée. En plus de sa fonction principale, la fonction prend la valeur de l’en-tête x-middleware-subrequest et l’utilise pour déterminer si l’intergiciel doit être appliqué. La valeur d’en-tête est divisée en une liste à l’aide d’un deux-points (:) comme séparateur, et la liste est vérifiée pour voir si elle contient une valeur middlewareInfo.name. Cela signifie que si nous ajoutons un en-tête x-middleware-subrequest avec la valeur correcte à la requête, alors l’intergiciel – quel que soit son objectif – sera complètement ignoré et la demande sera transférée via NextResponse.next() et le chemin vers la destination d’origine sera complété sans aucune influence de l’intergiciel. Cette tête et sa valeur sont comme une « clé maîtresse » qui contourne toutes les règles. À ce stade, nous avons réalisé que nous avons trouvé un problème étonnant et que les dernières pièces du puzzle doivent être complétées ensuite.
Pour que notre « clé universelle » fonctionne, sa valeur doit inclure middlewareInfo.name, mais quelle est cette valeur en réalité ?
Ordre d’exécution avec middlewareInfo.name
La valeur de middlewareInfo.name est très facile à deviner, c’est simplement le chemin où se trouve le middleware. Pour comprendre cela, nous devons d’abord avoir une idée simple de la manière dont le middleware était configuré dans les anciennes versions.
Tout d’abord, avant la version 12.2 - dans cette version, le contrat du middleware a changé - le fichier devait être nommé _middleware.ts. De plus, le routeur d’application (router) n’a été introduit que dans la version 13 de Next.js. Le seul routeur qui existait à l’époque était le routeur de pages, donc ce fichier devait être placé dans le dossier pages (spécifique au routeur).
Avec ces informations, nous pouvons déduire le chemin exact du middleware, ce qui nous permet de deviner la valeur de l’en-tête x-middleware-subrequest. Cette valeur est simplement composée du nom du répertoire (c’est-à-dire le nom du routeur unique qui existait à l’époque) et du nom du fichier, suivant la convention de nommage commençant par un soulignement à l’époque :
x-middleware-subrequest: pages/_middleware
Lorsque nous essayons de contourner ces intermédiaires configurés pour rediriger systématiquement les tentatives d’accès de /dashboard/team/admin vers /dashboard :
Ça a marché, on a ⚔️ envahi
Nous pouvons maintenant contourner complètement les intermédiaires, évitant ainsi tout système de protection basé sur eux, le plus typique étant l’autorisation, comme dans l’exemple ci-dessus. Cette découverte est assez étonnante, mais il y a d’autres points à considérer.
La version avant 12.2 permettait de placer un ou plusieurs fichiers _middleware à n’importe quel endroit de l’arbre des répertoires (à partir du dossier pages) pour les routes imbriquées, et ils avaient un ordre d’exécution, comme nous le voyons dans la capture d’écran des anciens documents récupérés depuis le Web Archive :
Qu’est-ce que cela signifie pour notre exploitation de vulnérabilité ?
Possibilité = Nombre de niveaux dans le chemin
Ainsi, pour accéder à /dashboard/panel/admin (protégé par un middleware), la valeur de middlewareInfo.name peut être l’une des trois possibilités, et la valeur de x-middleware-subrequest peut également être l’une des trois possibilités :
pages/_middleware
ou
pages/dashboard/_middleware
ou
pages/dashboard/panel/_middleware
Outil autorisé : Hier est devenu un poème, aujourd’hui en vaut encore plus la peine.
Jusqu’à présent, nous pensons que seules les versions antérieures à la version 13 sont vulnérables aux attaques, car le middleware a été déplacé dans le code source, et nous n’avons pas encore couvert tous ses aspects. Nous supposons que les mainteneurs ont dû remarquer cette vulnérabilité et l’ont corrigée avant les changements majeurs de la version 13, c’est pourquoi nous avons signalé cette vulnérabilité aux mainteneurs du cadre et avons poursuivi nos recherches.
Ce qui nous a beaucoup surpris, c’est qu’après deux jours de la première découverte, nous avons constaté que toutes les versions de Next.js - à partir de la version 11.1.4 - présentaient des vulnérabilités ! Le code n’est plus au même endroit, et la logique d’exploitation a également légèrement changé.
Comme mentionné précédemment, à partir de la version 12.2, le fichier ne contient plus de soulignement et doit être simplement nommé middleware.ts. De plus, il ne se trouve plus dans le dossier pages (ce qui est pratique pour nous, car à partir de la version 13, le routeur d’application a été introduit, ce qui aurait doublé les possibilités).
Cela dit, la charge utile pour les premières versions à partir de la version 12.2 est très simple :
En tenant compte de cela, la première version de la charge utile à partir de la version 12.2 est très simple :
x-middleware-subrequest: middleware
/src répertoire
Il faut également tenir compte du fait que Next.js offre la possibilité de créer un répertoire /src :
(Documentation de Next.js) En tant qu’alternative à avoir un répertoire app ou pages spécial dans le répertoire racine du projet, Next.js prend également en charge le placement du code de l’application dans un modèle courant sous le répertoire src. (Documentation de Next.js)
Dans ce cas, le payload sera :
x-middleware-subrequest: src/middleware
Ainsi, peu importe le nombre de niveaux dans le chemin, il n’y a que deux possibilités. Cela simplifie la difficulté d’exploiter les vulnérabilités des versions concernées.
Dans la dernière version, il y a eu un petit changement (nous le garantissons, c’est la dernière fois).
Profondeur de récursion maximale
Dans la version mise à jour, la logique a légèrement changé, veuillez consulter ce morceau de code :
v15.1.7
Comme auparavant, le système va récupérer la valeur de l’en-tête x-middleware-subrequest et former une liste en utilisant le deux-points comme séparateur. Mais cette fois, les conditions pour le transfert direct de la requête - c’est-à-dire ignorer les règles de middleware - sont différentes :
La valeur de la constante depth doit être supérieure ou égale à la valeur de la constante MAX_RECURSION_DEPTH (c’est-à-dire 5). Lors du processus d’assignation, chaque fois qu’une valeur dans la liste subrequests (c’est-à-dire les valeurs d’en-tête séparées par :) est égale à params.name (c’est-à-dire le chemin du middleware), la constante depth augmente de 1. Comme mentionné précédemment, il n’y a que deux possibilités : middleware ou src/middleware.
Ainsi, pour contourner le middleware, il suffit d’ajouter les en-têtes/valeurs suivants dans la requête :
x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware
ou
x-middleware-subrequest: src/middleware:src/middleware:src/middleware:src/middleware:src/middleware
À quoi servait initialement ce code ?
Ce code semble être destiné à empêcher les requêtes récursives de tomber dans une boucle infinie.
Exploitation de vulnérabilités
Puisque nous savons que vous aimez ce type de contenu, voici quelques cas réels du programme Bug Bounty.
Contourner l’autorisation/écrire à nouveau
Dans cet exemple, lorsque nous essayons d’accéder à /admin/login, nous recevons une réponse 404. D’après les en-têtes de réponse, il est clair que le middleware a effectué une réécriture d’URL pour empêcher l’accès aux utilisateurs non autorisés ou inappropriés :
Mais utilisez notre outil d’autorisation :
Nous pouvons accéder à ce point de terminaison sans aucune entrave, le middleware est complètement ignoré. Version cible de Next.js : 15.1.7
Contourner le CSP
Cette fois, le site utilise un middleware pour configurer - en plus d’autres fonctionnalités - le CSP et les cookies :
Évitons cela :
Version cible de next.js : 15.0.3Version cible de next.js : 15.0.3
Remarque : Veuillez prêter attention aux différences de payload des deux cibles, l’une utilisant le répertoire src/ tandis que l’autre ne l’utilise pas.
Réaliser un DoS par empoisonnement de cache (Quoi ? )
Oui, il est également possible de réaliser des attaques DoS par empoisonnement de cache à travers cette vulnérabilité. Ce n’est manifestement pas ce que nous cherchons en premier, mais si aucun chemin sensible n’est protégé et qu’il n’y a pas de points d’exploitation plus intéressants, alors certaines situations peuvent conduire à un déni de service par empoisonnement de cache (CPDoS) :
Supposons qu’un site Web réécrive le chemin de l’utilisateur en fonction de sa localisation géographique, en ajoutant (/en, /fr, etc.), et qu’il n’y ait pas de page ou de ressource fournie à la racine (/). Si nous contournons le middleware, nous éviterons la réécriture et arriverons finalement sur la page d’accueil. Étant donné que le développeur ne prévoyait pas que les utilisateurs accèdent à la page d’accueil, aucune page correspondante n’est fournie, ce qui nous donnera une erreur 404 (ou selon la configuration/type de réécriture, cela pourrait être une erreur 500).
Si le site utilise un système de cache/CDN, il se peut qu’il force la mise en cache des réponses 404, rendant la page inaccessible et ayant un impact négatif sur la disponibilité du site.
Clarifier
Depuis la publication du bulletin de sécurité, nous avons reçu des demandes de renseignements de la part de personnes qui s’inquiètent de la sécurité de leurs applications et qui ne comprennent pas tout à fait l’ampleur de l’attaque. Pour être clair, l’élément vulnérable est le middleware. Si vous n’utilisez pas de middleware (ou du moins si vous ne l’utilisez pas à des fins sensibles), il n’y a pas lieu de s’inquiéter (vérifiez l’aspect DoS mentionné ci-dessus, cependant), car le contournement du middleware ne contournera aucun mécanisme de sécurité réel.
Sinon, les conséquences pourraient être désastreuses, nous vous conseillons de mettre rapidement en œuvre les mesures recommandées dans l’avis de sécurité.
Annonce de sécurité - CVE-2025-29927
patch
Ce problème a été corrigé dans la version 15.2.3 de Next.js 15.x.
Ce problème a été corrigé dans Next.js 14.2.25.
Pour les versions de Next.js de 11.1.4 à 13.5.6, nous vous recommandons de consulter les solutions suivantes.
solution
Si vous ne pouvez pas mettre à niveau vers une version sécurisée, nous vous conseillons de bloquer les demandes des utilisateurs externes contenant l’en-tête de requête x-middleware-subrequest d’accéder à votre application Next.js.
gravité
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N (Severité : 9.1/10, critique)
Plus d’informations
Au moment de la rédaction de cet article, les applications déployées sur Vercel et Netlify ne sont manifestement plus affectées par cette vulnérabilité (mise à jour : en raison d’un grand nombre de faux positifs, Cloudflare a ajusté cette règle pour qu’elle ne soit effective que lorsque l’utilisateur l’active volontairement – ces faux positifs n’ont pas réussi à distinguer efficacement les demandes des utilisateurs légitimes de celles des attaquants potentiels).
Avertissement
Cette étude est publiée uniquement à des fins éducatives, visant à aider les développeurs à comprendre la cause fondamentale des problèmes, ou à inspirer les chercheurs / chasseurs de vulnérabilités dans leurs travaux de recherche futurs. Cet article sert de matériel complémentaire à l’avis de sécurité, fournissant des explications et clarifications supplémentaires concernant la nature de la vulnérabilité – car les en-têtes de requête qui ont conduit à cette vulnérabilité ont déjà été divulgués dans l’avis (ainsi que les différences de soumission associées).
Nous déclarons clairement que nous ne soutenons aucune utilisation immorale de ce document.
Conclusion
Comme souligné dans cet article, cette vulnérabilité existe dans le code source de Next.js depuis plusieurs années, évoluant avec les versions et les middleware. Tout logiciel peut présenter des vulnérabilités graves, mais lorsqu’elles touchent l’un des frameworks les plus populaires, cela devient particulièrement dangereux et peut avoir de graves conséquences sur un écosystème plus large. Comme mentionné précédemment, au moment de la rédaction de cet article, Next.js avait près de 10 millions de téléchargements par semaine. Il est largement utilisé dans des domaines critiques allant des services bancaires à la blockchain. Le risque est encore plus grand lorsque les vulnérabilités affectent des fonctionnalités matures sur lesquelles les utilisateurs comptent (comme l’autorisation et l’authentification).
L’équipe de Vercel a pris quelques jours pour résoudre cette vulnérabilité, mais il convient de noter qu’une fois qu’ils ont pris conscience du problème, le correctif a été soumis et fusionné dans une nouvelle version en quelques heures (y compris le portage vers l’arrière).
Chronologie :
27 février 2025 : Un rapport de vulnérabilité a été fait aux mainteneurs (nous pensions à l’époque que seules les versions 12.0.0 à 12.0.7 étaient concernées, et cela a été précisé dans le rapport).
1er mars 2025 : un deuxième e-mail a été envoyé, indiquant qu’en réalité, toutes les versions présentent des vulnérabilités, y compris la dernière version stable.
5 mars 2025 : Réception de la réponse préliminaire de l’équipe Vercel, indiquant que la version 12.x n’est plus maintenue (il est possible qu’ils n’aient pas encore lu le modèle d’annonce de sécurité joint dans notre deuxième e-mail, n’ayant pas remarqué que toutes les versions sont concernées).
5 mars 2025 : renvoyer le courriel, veuillez demander à l’équipe de consulter rapidement le deuxième courriel et le modèle d’annonce de sécurité.
11 mars 2025 : envoyer à nouveau un e-mail pour confirmer si les nouvelles informations ont été adoptées.
17 mars 2025 : Réponse de l’équipe Vercel reçue, confirmant que les informations pertinentes ont été prises en compte.
18 mars 2025 : réception d’un e-mail de l’équipe Vercel, indiquant que le rapport a été accepté et que le correctif a été terminé. Quelques heures plus tard, la version 15.2.3 contenant la correction (et incluant un correctif rétroactif) a été publiée.
21 mars 2025 : Annonce de sécurité officiellement publiée.
Dans l’ensemble, le processus de recherche de vulnérabilités zero-day n’est excitant et palpitant que lorsqu’un indice est découvert ; le reste du temps ressemble à un voyage rempli d’incertitudes - pour les curieux, cela peut apporter des gains de connaissances ; pour ceux qui manquent de patience, ce voyage semble particulièrement long. N’hésitez pas, agir en équipe est toujours beaucoup plus facile que de traverser le désert seul.