
Voici un scanner pour la CVE-2025-29927.
Ceci est un scanner de qualité professionnelle conçu pour détecter la vulnérabilité de contournement de middleware CVE-2025-29927 dans les applications Next.js.
X-Middleware-Subrequest fabriqués pour contourner le middleware Next.jspip install -r requirements.txt playwright install
### Exécuter le scanner```bash
python main.py --domain https://example.com --threads 10 --timeout 10 --save
python main.py --help
| Option | Description |
|----------------|--------------------------------------------|
| `--domain` | URL de base du site cible (obligatoire) |
| `--user-agent` | User-agent personnalisé (par défaut : chaîne Chrome) |
| `--timeout` | Délai d'expiration de la requête (par défaut : 10 secondes) |
| `--proxy` | Adresse du proxy (optionnel) |
| `--save` | Sauvegarder les résultats dans `results.txt` |
| `--threads` | Nombre de threads (par défaut : 10) |
| `--wordlist` | Worlist inclut Common Path |
---
## 🐳 Utilisation avec Docker
### Construire l'image Docker```bash
docker build -t cve-scanner .
docker run -it --rm cve-scanner --domain https://example.com --save
---
## ⚙️ GitHub Actions
Ce projet inclut un workflow GitHub Actions pour tester la configuration lors d'un push. Il :
- Installe les dépendances
- Installe les navigateurs Playwright
- Exécute une vérification `--help`
Voir `.github/workflows/python.yml`.
---
## 🧱 Structure```
.
├── main.py # Entry point
├── config.py # CLI parser
├── crawler.py # Playwright crawler
├── scanner.py # Multi-threaded vulnerability testing
├── requirements.txt
├── Dockerfile
└── .github/workflows
Aperçu de la Vulnérabilité CVE-2025-29927
CVE-2025-29927 est une faille de sécurité critique dans Next.js qui permet aux attaquants de contourner l'authentification et l'autorisation basées sur les middleware. En incluant un en-tête interne spécial (X-Middleware-Subrequest) dans les requêtes HTTP, un attaquant peut tromper le serveur Next.js en lui faisant sauter l'exécution du middleware, accédant ainsi à des routes protégées. En pratique, une requête qui serait normalement bloquée par le middleware d'authentification (par exemple en renvoyant un 401/403 ou en redirigeant vers la page de connexion) est traitée normalement si cet en-tête est présent, contournant ainsi les contrôles de sécurité. Cette vulnérabilité affecte les versions de Next.js 11.1.4 à 15.2.2, et les administrateurs sont invités à appliquer un correctif ou à mettre en œuvre des mesures d'atténuation (comme la suppression de cet en-tête au niveau des proxys) pour protéger leurs applications.
Détecter cette vulnérabilité dans une application web nécessite de découvrir les points de terminaison internes et de les tester avec l'en-tête malveillant pour voir si un accès non autorisé est possible. Vous trouverez ci-dessous un plan de conception pour un script Python avancé qui explore un site web cible (avec prise en charge complète de JavaScript) et recherche la CVE-2025-29927, répondant à toutes les exigences spécifiées.
Pour répondre à l'exigence d'une exploration approfondie incluant le contenu rendu par JavaScript, nous utiliserons Playwright (préféré à Selenium pour sa rapidité et son API moderne). Playwright est une puissante bibliothèque d'automatisation de navigateur sans tête qui peut gérer les applications web dynamiques et les frameworks JS modernes. Comparé à Selenium, Playwright offre une API plus moderne (basée sur le protocole Chrome DevTools) et prend en charge le fonctionnement synchrone et asynchrone, ce qui peut offrir de meilleures performances pour notre cas d'utilisation. Les bibliothèques clés et leurs instructions d'installation incluent :
playwright – pour l'automatisation du navigateur sans tête (pour charger les applications monopages ou les pages nécessitant JavaScript). (Installation : pip install playwright puis exécutez playwright install pour obtenir les binaires du navigateur).
requests ou httpx – pour envoyer des requêtes HTTP pendant la phase d'analyse. Nous pouvons utiliser requests pour la simplicité ou httpx/aiohttp pour le support asynchrone. (Installation : pip install requests ou pip install httpx).
bs4 (BeautifulSoup) – pour analyser le HTML et extraire les liens si nécessaire. Playwright peut interroger directement le DOM, mais utiliser BeautifulSoup sur le contenu HTML de la page est simple pour trouver les balises d'ancrage. (Installation : pip install beautifulsoup4).
concurrent.futures (intégré) ou asyncio – pour implémenter la concurrence. Pour le multithreading, le concurrent.futures.ThreadPoolExecutor de Python sera utilisé (aucune installation supplémentaire). Si une approche asynchrone est utilisée, le module asyncio de Python avec peut être utilisé pour les requêtes parallèles.
Justification : Playwright est choisi pour sa capacité à extraire du contenu dynamique sans complexité lourde. « À l'aide de Playwright, nous pouvons automatiser les navigateurs sans tête… pour naviguer sur le Web comme un humain, ce qui le rend idéal pour extraire des sites web dynamiques alimentés par JavaScript ». Cela garantit que notre explorateur peut voir les liens ou les éléments d'interface générés par les scripts (qu'un explorateur basé uniquement sur les requêtes manquerait).
Le module d'exploration utilisera Playwright en mode sans tête pour effectuer une exploration approfondie du site cible. L'objectif est de découvrir les chemins internes (points de terminaison) à tester, y compris ceux qui ne sont révélés qu'après l'exécution de JavaScript. Points clés de conception pour l'explorateur :
Navigation par Navigateur Sans Tête : Lancez une instance de navigateur (par exemple Chromium) en mode sans tête via Playwright. Utilisez un Browser Context avec un User-Agent personnalisé si spécifié par l'utilisateur (plus de détails dans la section suivante). Par exemple, nous pouvons créer un contexte avec browser.new_context(user_agent=<user_agent_string>) pour émuler le User-Agent choisi. Si un proxy est configuré, appliquez-le au lancement (Playwright permet de définir un serveur proxy lors du lancement du navigateur ou du contexte).
Stratégie d'Exploration Récursive : Commencez à partir d'une URL de base donnée (graine). Utilisez page.goto(base_url, timeout=<T>) pour charger la page (délai d'attente configurable). Attendez que le réseau soit inactif ou un court délai pour permettre au contenu dynamique de se charger si nécessaire. Ensuite, extrayez les liens. Nous pouvons extraire les liens soit :
links = page.evaluate("Array.from(document.querySelectorAll('a[href]'), a => a.href)"), ou<a href>.Filtrage des Liens : Filtrez les liens qui ne font pas partie du domaine cible (pour rester interne). Ignorez également les URL de fichiers statiques telles que les images, CSS, JS, etc. Par exemple, ignorez toute URL avec des extensions de fichiers comme .css, .js, .jpg, .png, .gif, .svg, .woff, etc. Une approche pratique (inspirée du modèle ProjectDiscovery) consiste à ignorer tout chemin contenant un « point » après la barre oblique initiale. Ils ont extrait les points de terminaison avec un modèle regex href=['"](https://github.com/houmanpashaei/cve-2025-29927/blob/main/%5C/%5B%5E.%5C%22%27%5D%2B)['"] – cela capture les chemins internes qui ne contiennent pas de point (ignorant ainsi les ressources). Nous implémenterons une logique similaire dans le code pour éviter de mettre en file d'attente des ressources statiques ou des liens externes.
Suivi et Contrôle de la Profondeur : Maintenez un ensemble d'URL visitées pour éviter les boucles infinies ou les répétitions. Utilisez une file d'attente (FIFO) pour le parcours BFS du graphe de liens du site. Optionnellement, permettez à l'utilisateur de spécifier une limite de profondeur d'exploration ou un nombre maximum de pages à visiter pour éviter de s'exécuter indéfiniment sur de grands sites.
Contenu Rendu par JavaScript : Parce que nous utilisons un vrai navigateur, même les liens ajoutés au DOM par des scripts (par exemple, une application React qui rend un menu après avoir récupéré des données) seront visibles pour notre explorateur. Nous pourrions envisager de cliquer ou d'interagir si nécessaire (par exemple, si certaines pages ne se chargent qu'après une action utilisateur). Cependant, pour rester simple et rapide, la conception initiale se concentrera sur la collecte des hrefs des balises <a> sur chaque page chargée. Nous pourrons améliorer plus tard pour gérer des choses comme le défilement infini ou le contenu derrière des clics si l'application cible l'exige.
Efficacité : Playwright prend en charge l'exécution de plusieurs pages/onglets en parallèle via son API asynchrone. Nous pourrions instancier plusieurs pages avec asyncio.gather pour récupérer plusieurs liens simultanément. Pour une implémentation initiale, une approche plus simple consiste à explorer séquentiellement (ce qui est plus facile à implémenter) et à s'appuyer sur l'analyse multithreadée pour les performances. Si nécessaire, une optimisation avancée pourrait impliquer une exploration asynchrone (en utilisant async with async_playwright() et en attendant plusieurs appels à page.goto). Mais comme l'automatisation du navigateur est plus lourde en ressources, une approche prudente consiste à ne garder qu'une ou quelques pages de navigateur à la fois pour éviter de surcharger le système.
Le script présentera un menu de configuration convivial au démarrage, permettant à l'utilisateur de personnaliser les paramètres d'analyse ou d'accepter les valeurs par défaut. Cela peut être fait via un menu console interactif (en utilisant des invites input()) ou via des arguments de ligne de commande (en utilisant argparse pour une interface CLI plus professionnelle). Les options incluent :
User-Agent Personnalisé : L'utilisateur peut spécifier une chaîne User-Agent personnalisée pour l'explorateur et l'analyseur. Cela sera appliqué au contexte du navigateur Playwright et à toutes les requêtes HTTP directes. L'utilisation d'un User-Agent non par défaut peut aider à éviter une détection triviale de bot. (Par défaut, Playwright pourrait utiliser quelque chose d'identifiable ; nous pouvons le remplacer facilement comme montré ci-dessus.) Par exemple, l'utilisateur pourrait saisir une chaîne s'identifiant comme Chrome sur Windows, que nous transmettrons à la création du contexte Playwright.
Délai d'Attente des Requêtes : L'utilisateur peut définir un délai d'attente (en secondes) pour les chargements de page et les requêtes HTTP. Cela empêche l'analyseur de rester bloqué trop longtemps sur des points de terminaison qui ne répondent pas. Nous appliquerons ce paramètre dans page.goto(timeout=...) pour l'exploration, et dans les requêtes (par exemple, requests.get(timeout=...)) pour l'analyse.
Paramètres Proxy : Si l'utilisateur souhaite router le trafic via un proxy (pour l'anonymat ou pour atteindre des hôtes internes), il peut saisir l'URL du proxy (et les identifiants si nécessaire). Le script configurera le navigateur Playwright pour utiliser ce proxy au lancement (par exemple, browser.launch(proxy={"server": "http://<proxy_host>:<port>", "username": "...", "password": "..."}) comme montré dans les exemples). De même, pour les requêtes, nous définirons le paramètre proxies (ou les variables d'environnement) en conséquence.
Sortie vers un Fichier : Le menu demandera si l'utilisateur souhaite enregistrer les résultats dans un fichier (par exemple, results.txt). Si oui, le script écrira les points de terminaison vulnérables découverts et les détails dans ce fichier en plus de les afficher à l'écran. Sinon, les résultats seront simplement imprimés sur stdout. (Nous pourrions éventuellement consigner tous les chemins analysés dans un journal détaillé si nécessaire, mais le fichier enregistrerait spécifiquement les positifs ou le rapport complet selon la préférence de l'utilisateur.)
Autres Options : Nous pouvons inclure des bascules comme « Mode verbeux » pour la journalisation de débogage, ou « Profondeur/pages max d'exploration » si nécessaire. Cela peut aider l'utilisateur à affiner l'analyse. Pour le périmètre initial, les quatre options principales ci-dessus suffisent.
Le système de menu sera implémenté dans un module de configuration/installation dédié. Cela pourrait simplement être une fonction qui imprime des invites et collecte les entrées, avec des valeurs par défaut sensées si l'utilisateur appuie sur Entrée (par exemple, User-Agent par défaut vers un standard, délai d'attente par défaut = 10 secondes, pas de proxy, pas de sortie fichier). Cela garde l'interaction claire et permet au script de s'exécuter également de manière non interactive (si nous ajoutons plus tard des arguments de ligne de commande, nous pouvons contourner les invites interactives en fournissant toute la configuration nécessaire via les arguments).
Les performances sont cruciales pour un analyseur, surtout si de nombreux points de terminaison sont trouvés. Le script emploiera la concurrence pour la vitesse, soit via le multithreading, soit via asyncio (ou une combinaison) :
Analyse Multithreadée : Étant donné que l'analyse des chemins découverts (envoi de requêtes HTTP avec en-têtes) est une tâche liée aux E/S, nous pouvons utiliser en toute sécurité les threads Python pour la paralléliser. Les opérations d'E/S libèrent le Global Interpreter Lock, ce qui permet à plusieurs threads de progresser sur les requêtes réseau simultanément. En utilisant concurrent.futures.ThreadPoolExecutor, nous pouvons avoir un pool de threads de travail, chacun gérant un sous-ensemble des tâches d'analyse. Cela peut considérablement accélérer le processus : par exemple, l'exécution de 5 threads en parallèle pourrait réduire le temps d'analyse d'un facteur d'environ 5, comme le montrent d'autres contextes de scraping web. Nous permettrons au nombre de threads d'être configurable ou choisirons une valeur par défaut raisonnable (comme 10 threads) en équilibrant vitesse et charge serveur. Chaque thread prendra des URL d'une file d'attente partagée de points de terminaison à tester.
Alternative Asynchrone : Alternativement, une approche asynchrone peut être utilisée, surtout si nous utilisons Playwright en mode asynchrone ou httpx pour les requêtes HTTP. Nous pourrions await plusieurs requêtes simultanément. Par exemple, httpx.AsyncClient peut envoyer de nombreuses requêtes simultanément et rassembler les résultats. Cette approche évite la surcharge des threads et peut être très efficace pour un grand nombre de points de terminaison. Cependant, mélanger asyncio avec Playwright (qui peut lui-même être utilisé de manière asynchrone) pourrait compliquer les choses. Une solution pragmatique consiste à utiliser le threading pour la phase d'analyse HTTP (car l'exploration avec Playwright pourrait être plus facile à gérer en mode synchrone).
Exploration Concurrente : Nous devrions également envisager de paralléliser l'exploration si le site est volumineux. Playwright peut ouvrir plusieurs pages à la fois en utilisant un contexte asynchrone. Nous pourrions implémenter une concurrence limitée (par exemple, 2-3 pages à la fois) pour l'exploration. Par exemple, à mesure que nous extrayons de nouvelles URL, nous pourrions lancer une nouvelle Page pour chacune si nous utilisons asyncio. Cela peut être une optimisation avancée si nécessaire. Initialement, une exploration monothread est plus simple et suffisante pour des sites de taille modérée, mais la conception peut noter cela comme un point d'amélioration.
En résumé, la concurrence sera principalement appliquée à la phase d'analyse pour tester plusieurs points de terminaison en parallèle. Cela rend l'analyse beaucoup plus rapide sans sacrifier la précision (car chaque requête est indépendante). Comme le note une référence, « Le multithreading avec concurrent.futures peut donner un coup de pouce significatif ici. Nous pouvons exécuter des tâches d'E/S simultanément sur plusieurs threads et constater une forte accélération ». Le multithreading est approprié ici car les tâches liées au réseau en bénéficient même en Python.
Le cœur du script est le module d'analyse, qui prend la liste des points de terminaison découverts (chemins) et vérifie chacun pour des signes de la vulnérabilité CVE-2025-29927. Le processus pour chaque point de terminaison sera :
x-middleware-rewrite, x-middleware-next ou x-middleware-redirect, cela suggère que cette route est protégée par un middleware. Nous vérifions également si le statut n'est pas 200 (ce qui signifie que l'accès a été refusé ou redirigé), car ce sont ceux qui sont susceptibles d'être contournés. (Si le statut est déjà 200 et que le contenu se charge normalement, il s'agit soit d'une page publique, soit la vulnérabilité ne s'applique pas ; nous pourrions quand même la tester, mais le véritable intérêt réside dans les pages protégées.)X-Middleware-Subrequest. Nous essaierons une variété de valeurs d'en-tête pour assurer la détection sur toutes les versions de Next.js :Une valeur générique comme "1" ou "true" (certaines sources suggèrent que simplement définir l'en-tête à n'importe quelle valeur déclenche le saut).
La charge utile spécifique utilisée dans les exploits publics, par exemple "middleware:middleware:middleware:middleware:middleware" (cinq répétitions de "middleware"). C'est connu pour induire le contournement pour les versions récentes (13+). Nous inclurons exactement cette valeur.
La sortie du script doit être facile à lire et à interpréter, et éventuellement sauvegardée dans un fichier. Nous formaterons la sortie console avec des titres clairs et une indentation appropriée. Quelques considérations :
Après le scan, afficher un résumé des résultats. Par exemple : « Scan terminé : 3 points de terminaison vulnérables trouvés (sur 45 testés). » Ensuite, lister les points de terminaison vulnérables avec les détails.
Utiliser un format cohérent pour chaque ligne de résultat, comme montré ci-dessus, éventuellement avec des balises [VULNERABLE] pour attirer l'attention. Si on utilise une bibliothèque comme Rich, on pourrait même colorer « VULNERABLE » en rouge ou jaune. Même sans bibliothèques supplémentaires, on peut utiliser les codes ANSI via colorama pour mettre en évidence, ou simplement du texte en majuscules.
Si aucune vulnérabilité n'est trouvée, le dire explicitement : « Aucune vulnérabilité détectée pour CVE-2025-29927. »
Si les résultats doivent être sauvegardés, assurez-vous qu'ils sont écrits dans un format similaire au fichier. Éventuellement d'une manière légèrement plus verbeuse ou en CSV pour une utilisation programmatique, mais puisque l'utilisateur a spécifiquement mentionné un fichier texte, nous écrirons probablement les mêmes lignes dans results.txt.
De plus, toute erreur critique ou exception rencontrée (comme l'impossibilité de charger une certaine page) peut être signalée dans la sortie de manière élégante (au lieu d'une trace de pile). Nous pouvons attraper les exceptions et imprimer un avertissement d'une ligne par URL en échec : par exemple, « Timeout lors du chargement de /blog (ignoré) ». Ainsi, l'utilisateur sait si certains chemins n'ont pas été testés.
Tout au long de l'exécution, nous pourrions afficher un spinner ou une progression (pour les longues exécutions) ou au moins imprimer quelle page est en cours de crawl ou quel point de terminaison est testé, si le mode verbose est activé. Pour une sortie plus propre, nous pourrions seulement montrer les cas vulnérables découverts à la fin, mais un journal en cours (écrivant peut-être dans un fichier de log séparé) peut aider à la transparence.
Compte tenu de l'accent mis sur un format clair, l'utilisation de puces ou d'une disposition en tableau pourrait aider lors de l'impression de plusieurs résultats :
Nous pourrions tabuler comme : Endpoint | Base Status | Base Length | Bypass Status | Bypass Length | HeaderValueUsed | Result
Cependant, une forme de phrase simple pourrait être plus lisible pour un large éventail d'utilisateurs. Nous veillerons à ce que chaque résultat soit sur une nouvelle ligne et étiqueté clairement.
En fournissant à la fois une sortie écran et une sauvegarde facultative dans un fichier, l'outil est utile à la fois pour une utilisation interactive et pour un scan automatisé (où l'utilisateur peut ensuite consulter le fichier ou l'intégrer dans des rapports).
Pour rendre le script maintenable et de qualité professionnelle, nous organiserons le code en modules, chacun gérant un aspect distinct de la fonctionnalité. Une structure de projet possible :
crawler.py : Contient la logique de crawl utilisant Playwright. Il aura des fonctions comme crawl_site(start_url, config) -> List[str] qui renvoie une liste des chemins internes découverts. Ce module gérera le lancement du navigateur, la récupération des pages, l'extraction des liens et l'application des filtres (domaine, exclusion des fichiers statiques). Il pourrait également contenir une logique auxiliaire pour normaliser les URL (par exemple, supprimer les fragments d'URL, gérer les chemins relatifs via urllib.parse.urljoin).
scanner.py : Contient la logique de scan pour la vulnérabilité. Il inclura des fonctions telles que scan_paths(url_list, config) -> List[ScanResult]. Cela gérera la création de requêtes HTTP (en utilisant requests.Session ou un client httpx), l'application des en-têtes, la comparaison des réponses et la collecte des résultats. Si on utilise le multithreading, ce module créera le ThreadPool et gérera les tâches. Il pourrait définir une petite classe de données ScanResult pour contenir les informations sur chaque chemin (path, vulnerable: bool, details).
config.py (ou settings.py) : Contient le code pour le menu utilisateur et la configuration. Par exemple, une fonction qui interagit avec l'utilisateur et renvoie un objet/dictionnaire de configuration avec tous les paramètres choisis (user_agent, timeout, proxy, indicateur output_file, etc.). Si on utilise des arguments CLI, ce module pourrait alternativement parser . Essentiellement, cette partie isole toute la saisie utilisateur et la gestion de la configuration.
Chaque module sera conçu pour être modulaire et réutilisable. Par exemple, on pourrait réutiliser crawler.py pour obtenir des liens de site à d'autres fins, ou réutiliser scanner.py pour tester cette vulnérabilité sur une liste donnée d'URL (même sans crawl).
Gestion des exceptions et arrêt gracieux : Nous mettrons en place une gestion robuste des exceptions :
Entourer les opérations réseau de try/except (attraper les timeouts, erreurs de connexion, etc.). Si le crawl d'une page échoue, journaliser et continuer avec les autres. Si une requête de scan échoue (par exemple, erreur de proxy), marquer ce point de terminaison comme erreur mais continuer à scanner les autres.
Utiliser des blocs finally ou des gestionnaires de contexte pour garantir le nettoyage des ressources. Par exemple, utiliser le contexte async_playwright() ou s'assurer que browser.close() est appelé à la fin du crawl. De même, garantir que les descripteurs de fichiers sont fermés après l'écriture.
Gérer KeyboardInterrupt (Ctrl+C) : Nous pouvons intercepter le KeyboardInterrupt dans la boucle principale et initier un arrêt gracieux – par exemple, imprimer « Arrêt, nettoyage en cours… », arrêter les threads (peut-être en utilisant ThreadPoolExecutor.shutdown(wait=False) pour arrêter le lancement de nouvelles tâches), et fermer le navigateur. Cela évite les processus orphelins ou les fichiers verrouillés si l'utilisateur interrompt.
Utiliser la journalisation pour les messages de débogage (peut-être via la bibliothèque logging de Python). Dans un outil professionnel, vous auriez des niveaux de journalisation ; par exemple, les logs de débogage pourraient inclure chaque requête effectuée, tandis que le niveau info ne montre que la progression de haut niveau. L'utilisateur pourrait définir un indicateur verbose pour basculer cela. Par défaut, nous pourrions journaliser un minimum d'informations pour ne pas submerger la sortie.
Qualité du code : Nous adhérerons aux meilleures pratiques de codage :
Suivre les directives de style PEP8 pour la lisibilité.
Utiliser des noms de fonctions et de variables significatifs.
Ajouter des docstrings aux fonctions expliquant leur objectif et leur utilisation.
Utiliser des indications de type pour les signatures de fonctions (annotations de type Python 3) pour rendre le code plus facile à comprendre et pour détecter les problèmes de type tôt.
Modulariser les constantes (comme la liste des charges utiles d'en-tête, les listes d'extensions de fichiers statiques à ignorer, etc.) en haut ou dans une config, afin qu'elles puissent être facilement mises à jour. Par exemple, HEADER_PAYLOADS = ["middleware:middleware:...","src/middleware:..."] etc., définies à un seul endroit.
Inclure éventuellement des tests unitaires pour certaines fonctions auxiliaires (s'il s'agissait d'un projet plus vaste, bien que pour un outil à script unique cela puisse être sauté ; néanmoins, concevoir avec la testabilité en tête est bénéfique).
Améliorations de qualité professionnelle : Pour rendre le script plus robuste et prêt pour la production, nous pouvons également considérer :
Prise en charge de l'authentification : Permettre à l'utilisateur de fournir des cookies ou des identifiants s'il souhaite scanner une section authentifiée du site (même si la vulnérabilité consiste à contourner l'auth, il peut y avoir des scénarios où vous devez d'abord vous connecter pour atteindre certains liens afin de tester ensuite le contournement – bien que le contournement fonctionne probablement sans auth valide, cela pourrait aider à crawler des liens profonds qui ne sont pas publics).
Fichier de configuration : Au lieu de (ou en plus de) une saisie interactive, permettre la lecture des options à partir d'un fichier de configuration ou de variables d'environnement, ce qui est utile pour les déploiements automatisés du scanner.
Formats de sortie : Fournir une sortie dans plusieurs formats tels que JSON ou CSV pour l'intégration avec d'autres outils. Par exemple, un drapeau --json pourrait vider les résultats en JSON lisible par machine.
Intégration avec les frameworks existants : La logique pourrait être intégrée dans un framework de scan plus large (par exemple, en faire un module pour OWASP ZAP ou s'intégrer avec Nuclei de ProjectDiscovery en produisant un rapport compatible). Au minimum, assurez-vous que la sortie du script identifie clairement la vulnérabilité et les URL affectées afin qu'elle puisse être utilisée dans les rapports.
Sessions de navigateur parallèles : Si vous ciblez de très grandes applications, envisagez de lancer plusieurs contextes de navigateur en parallèle pour crawler différentes sections simultanément. Playwright peut gérer plusieurs contextes (chaque contexte est isolé, similaire à des profils de navigateur séparés). Cela pourrait accélérer considérablement le crawl au prix d'une utilisation plus élevée des ressources.
Dégradation gracieuse : Si Playwright échoue (par exemple, l'environnement manque d'affichage ou d'installation correcte), le script pourrait revenir à un crawl plus simple basé sur requests (qui pourrait manquer certains liens mais qui vaut mieux que rien). Cela rend l'outil plus robuste dans divers environnements. De même, si la concurrence est réglée trop haut et cause des problèmes, attraper ces problèmes et suggérer à l'utilisateur de réduire le nombre de threads.
En suivant une structure propre et ces bonnes pratiques, le script sera plus facile à maintenir et à étendre. Chaque composant peut être travaillé indépendamment – par exemple, améliorer la capacité du crawler à analyser la navigation lourde en JavaScript, ou mettre à jour le scanner avec de nouvelles variations de charges utiles d'en-tête si des recherches futures trouvent des modèles d'exploitation supplémentaires.
En conclusion, cette conception décrit une approche complète pour détecter CVE-2025-29927 dans les applications web. Elle utilise un navigateur sans tête pour un crawl profond, le multithreading pour un scan efficace, et des pratiques de codage robustes pour la fiabilité. En comparant les réponses avec et sans l'en-tête spécial, elle peut identifier de manière fiable les points de terminaison vulnérables où le middleware Next.js est contourné. Le résultat est un outil de qualité professionnelle qui aide les ingénieurs en sécurité et les développeurs à trouver et à traiter rapidement cette vulnérabilité critique dans leurs applications.
httpx(Facultatif) argparse – pour analyser les arguments de ligne de commande si nous voulons une interface CLI au lieu d'un menu interactif. (module intégré)
(Facultatif) rich ou colorama – pour une sortie console colorée ou formatée afin d'améliorer la lisibilité. (Installation : pip install rich ou pip install colorama).
Sécurité des Threads : Nous assurerons une gestion thread-safe des données partagées. La liste des URL à analyser peut être traitée avec ThreadPoolExecutor.map pour simplifier, ou nous pouvons utiliser une file d'attente thread-safe (queue.Queue de Python) et faire en sorte que les threads en tirent jusqu'à épuisement. L'ensemble visited pour l'exploration n'est accédé que par l'explorateur (thread unique, sauf si nous faisons de l'exploration concurrente). Les threads d'analyse ne liront que leur liste d'URL (aucune modification de structures partagées, sauf peut-être la journalisation des résultats, que nous pouvons protéger avec un verrou ou simplement collecter dans une liste thread-safe).
Limitation du Débit et Politesse : Comme il s'agit d'un outil de test de sécurité, la vitesse est une priorité, mais nous pouvons toujours vouloir éviter de surcharger la cible. L'utilisateur peut être invité à définir un nombre de threads raisonnable. Nous pouvons également implémenter un petit délai ou utiliser des sémaphores pour limiter la concurrence si nécessaire. Par exemple, nous pourrions ne pas lancer tous les threads à la fois si le réseau de l'utilisateur ou le serveur risque de s'étouffer. Dans un scénario avancé, une approche asynchrone pourrait utiliser un sémaphore pour autoriser, disons, 5 requêtes simultanées à la fois. Ces détails peuvent être ajustés en fonction des tests de performance du script.
La charge utile alternative pour les projets utilisant le répertoire /src, par exemple "src/middleware:src/middleware:src/middleware:src/middleware:src/middleware"
Optionnellement, des valeurs à un seul segment comme "middleware" ou "src/middleware" pour être complet (les versions plus anciennes de Next.js pourraient utiliser un fichier _middleware dans le répertoire pages, avec une charge utile légèrement différente nécessaire, mais les charges utiles à segments multiples ci-dessus couvrent largement les cas connus).
Chacune de ces requêtes sera effectuée avec l'en-tête personnalisé défini. Nous nous assurons également d'utiliser la même méthode (GET) et d'inclure tous les en-têtes de la référence qui pourraient être nécessaires (comme les cookies ou les jetons d'authentification si l'utilisateur en a fourni pour une analyse connectée, bien que généralement nous analysions sans authentification).
Si la référence était une erreur ou une redirection (par exemple, 401 Non autorisé, 403 Interdit, ou une redirection vers la connexion) et l'une des réponses avec en-tête est 200 OK avec un corps significativement plus grand (ou indiquant que la page s'est chargée), c'est un indicateur fort de vulnérabilité. Par exemple, si /admin renvoyait 403 normalement, mais avec l'en-tête renvoie 200 et contient le HTML du tableau de bord admin, nous le signalons.
Dans certains cas, la différence peut être un 302 contre un 200, ou un 404 contre un 200. Nous considérerons un changement de code d'état d'un non-200 à 200 comme un signe probable. De plus, si le statut reste 200 mais que la longueur du contenu change radicalement, cela pourrait indiquer que l'en-tête a altéré le comportement (moins courant pour ce bug particulier, mais une possibilité si la page livrait normalement une chose et avec l'en-tête en livrait une autre).
Nous implémenterons des vérifications telles que : if base_status_code != 200 and test_status_code == 200: (et éventuellement aussi s'assurer que test_body_length > base_body_length ou contient un mot-clé authentifié) puis signaler comme vulnérable. Si la référence était une redirection (par exemple, 307 vers /login) et que le test donne 200, également signaler. Essentiellement, « l'accès était-il précédemment refusé mais maintenant autorisé ? ».
Si le statut de réponse avec l'en-tête est 404 ou 500 alors que la référence était une redirection, cela pourrait être le scénario d'empoisonnement du cache (contournement de la redirection du middleware provoquant un 404 à l'origine). Ce scénario est un peu plus difficile à détecter avec une seule requête, mais la présence d'un 404 avec en-tête alors que la référence était une redirection pourrait également être notée (bien que ce ne soit pas un contournement d'authentification, c'est quand même un effet de la vulnérabilité). Notre objectif est cependant de détecter un contournement d'authentification (accès 200 OK).
results.txt si l'utilisateur a opté pour la sauvegarde des résultats. Nous devrions formater cela clairement, par exemple :[*] /admin -> baseline 403, with X-Middleware-Subrequest (payload X) got 200 [VULNERABLE]
Nous pouvons également afficher quelque chose comme la longueur de la réponse ou un extrait de la réponse pour confirmer (peut-être juste la longueur par souci de concision, par exemple, « len: 0 -> 10240 bytes »). Si plusieurs charges utiles ont été essayées, nous pourrions lister lesquelles ont réussi.
Si le site ne semble pas être une application Next.js (par exemple, nous n'avons trouvé aucune occurrence de /_next/static/ sur la page d'accueil, ce qui est un signe révélateur), nous pourrions afficher une note : « Aucun indicateur Next.js trouvé, la cible n'utilise peut-être pas Next.js – probablement non vulnérable. » Mais nous pouvons toujours procéder génériquement, car une vérification Next.js est une optimisation plutôt qu'une nécessité.Cette logique sera encapsulée proprement. Par exemple, nous pourrions avoir une fonction scan_endpoint(url, session, header_payloads) qui renvoie un objet de résultat ou un dict indiquant si elle est vulnérable et les détails. Nous intégrerons des vérifications robustes pour éviter les faux positifs. Plus précisément, exiger un changement de code d'état vers 200 (ou autre preuve claire) permet de s'assurer que nous ne signalons que les contournements réels. Comme indiqué dans l'analyse de ProjectDiscovery, le scanner vérifie le statut de réponse 200 lorsque l'en-tête spécial est inclus pour confirmer la vulnérabilité.
get_user_config()argparse.ArgumentParserutils.py : Fonctions utilitaires, par exemple pour imprimer des bannières, formater les chaînes de sortie, gérer la sortie en couleur, ou une aide courante comme is_static_resource(url) (pour vérifier si une URL pointe probablement vers un fichier statique). Pourrait également inclure une fonction d'arrêt gracieux (à appeler sur SIGINT).
main.py : Le script d'entrée qui relie tout ensemble. Il va :
config.py).main pourrait simplement être en bas d'un seul fichier, mais pour la propreté, la séparation est meilleure.