Skip to content
KitploitKITPLOIT
OutilsBlog
Soumettre
OutilsBlog
Soumettre

Outils de Hacking, PenTest et Cybersécurité pour votre Arsenal de Sécurité !

Kitploit est un répertoire d'outils de hacking, de cybersécurité et de pentesting. Découvrez les dernières mises à jour des projets pour trouver des vulnérabilités, analyser des systèmes, automatiser les tests et renforcer votre sécurité.

··Flux·Contact·Confidentialité·© 2026 Kitploit

Répertoire d'outils

Catégories

Voir toutes les catégories
Loading categories
crw-PoC — PoC — SSRF via contournement du filtre de sécurité d'URL par la couche de rendu JS dans crw (GHSA-5jp3-339h-vxqw, CVE-2026-87007, CVSS 7.5). | Kitploit
Outils/GitHubGitHub/squeeze440/crw-poc
Analyse des VulnérabilitésExploitationSécurité WebTests d'Intrusion
GitHubsqueeze440/crw-poc

crw-PoC

PoC — SSRF via contournement du filtre de sécurité d'URL par la couche de rendu JS dans crw (GHSA-5jp3-339h-vxqw, CVE-2026-87007, CVSS 7.5).

Voir le dépôt
il y a 7 joursPas encore vérifié

Populaires

Voir tout →

Découvrez les outils les plus utilisés par notre communauté.

Explorer tous les outils

Parcourez notre collection d'outils

Voir tous les outils →
Partager

crw : avis de sécurité

Statut CVE : demandé, en attente d'attribution. Cette découverte est publiée sous GHSA-5jp3-339h-vxqw. Lors de l'attribution de la CVE, ce dépôt sera renommé CVE-YYYY-NNNNN-crw-PoC et cette bannière sera remplacée par le lien CVE.

ChercheurDostxodjayev Abdullox (@squeeze440)
AvisGHSA-5jp3-339h-vxqw
CVSS 3.17.5 (Élevé)
FaiblesseCWE-918

Résumé

Dans crw-server, la liste d'autorisation/de refus SSRF (crw_core::url_safety) n'est appliquée qu'une seule fois, contre l'url fournie par l'appelant, avant que la requête ne soit transmise à un moteur de rendu ; les niveaux de rendu JS pilotés par CDP (LightPanda — le moteur de rendu JS par défaut — et Chrome) pilotent ensuite la page cible via Page.navigate et suivent chaque redirection ultérieure, navigation déclenchée par JS, et XHR/fetch dans la page entièrement à l'intérieur de la propre pile réseau du navigateur, sans aucun appel supplémentaire à url_safety à aucun moment, permettant à un appelant authentifié (ou, sur un déploiement par défaut sans clé, non authentifié) qui définit renderJs:true de faire pivoter le crawler vers l'espace d'adressage RFC1918/loopback/link-local via une seule redirection ouverte externe et de lire en retour la réponse du service interne dans le corps de la réponse API.

Produit

fastCRW / crw — crw-server (API REST, également accessible de manière identique via la couche d'outils MCP in-process crw-mcp, qui appelle le même chemin de code crw_server::routes::mcp::call_tool).

Version testée

Commit 9f1e5ea5555bbf29cd41e454902be0cd804a15e4 (v0.28.0, pointe de main au moment du test), construit et exécuté via le propre docker-compose.yml --profile heavy du projet.

CVSS v3.1 estimé

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N — 7.5 (Élevé)

  • PR:N : le déploiement auto-hébergé par défaut livré (config.docker.toml) n'a pas de section [auth]/api_keys, correspondant au défaut « ouvert par conception » déjà documenté dans GHSA-qq8c-fch4-cxq7. Un déploiement qui configure des clés API rétrograde cela en PR:L (tout locataire authentifié unique peut toujours faire pivoter le moteur partagé vers n'importe quel réseau interne sur lequel il s'exécute) — le bug n'est pas atténué par l'ajout de clés, seulement re-conditionné.
  • I:N/A:N : le PoC démontre la lecture en retour d'une réponse HTTP interne (confidentialité). Aucune action d'écriture/modification d'état contre une cible interne n'a été exercée ou n'est revendiquée.
  • S:U : la divulgation est entièrement médiée par le propre canal de réponse API du composant vulnérable.

Détails

Cause racine : le chemin du moteur de rendu CDP/navigateur n'appelle jamais crw_core::url_safety après l'unique vérification effectuée au niveau de la couche de routage.

  • crates/crw-server/src/routes/scrape.rs:29-34 (de manière identique dans crawl.rs:47/136, map.rs:73/58, extract.rs:178/95, batch.rs:111/102, v2/*, routes/mcp.rs:25) — la seule vérification SSRF dans tout le cycle de vie de la requête pour une requête rendue en JS : crw_core::url_safety::validate_safe_url_resolved(&parsed_url) s'exécute une fois contre l'url littérale de l'appelant, avant que le moteur de rendu ne soit jamais invoqué.
  • crates/crw-renderer/src/cdp.rs:2621-2631 — le future work de fetch_inner envoie Page.navigate avec l'url brute directement au point de terminaison CDP (partagé par les niveaux chrome et lightpanda — les deux parlent CDP via cette même fonction). Chrome/LightPanda effectue ensuite sa propre résolution DNS et suit toute redirection côté serveur, <meta http-equiv="refresh">, ou changement de location JS en interne. Aucun appel à url_safety n'existe nulle part dans cdp.rs.
  • crates/crw-renderer/src/blocklist.rs:1-124 — la seule logique d'interception Fetch.requestPaused par requête qui s'exécute pendant un rendu CDP (câblée dans cdp.rs autour des lignes 930-1002). Elle filtre les hôtes publicitaires/traceurs et les types de ressources bloqués (images, polices, etc.) pour des raisons de bande passante/bruit uniquement — elle ne vérifie jamais la destination d'une requête par rapport aux plages privées/loopback/link-local.
  • crates/crw-crawl/src/single.rs:559-569 — le seul endroit où le final_url post-navigation est inspecté. redirect_is_material() décide uniquement s'il faut attacher une chaîne cosmétique "redirected_to: <url>" à data.warnings (pour signaler « vous avez obtenu la mauvaise page », selon le commentaire référençant northernair.ca) — il n'appelle jamais url_safety::validate_safe_url* et ne fait jamais échouer la requête.
  • Contraste : crates/crw-renderer/src/http_only.rs:291 — le niveau HTTP simple (non-JS) enveloppe correctement son reqwest::Client dans crw_core::url_safety::safe_redirect_policy(), qui re-valide (avec résolution DNS) chaque saut de redirection. C'est exactement la protection manquante dans les niveaux CDP. Confirmé dynamiquement : la même requête PoC avec renderJs:false est correctement rejetée ("error following redirect", voir la capture d'écran de référence).

Preuve de concept

Confirmé dynamiquement contre la propre pile docker-compose.yml --profile heavy du projet (crw + lightpanda + chrome, construite à partir du commit testé).

  1. Mise en place d'un substitut de « service interne » sur le même réseau bridge Docker que crw/chrome/lightpanda (docker network: source_default), à l'adresse privée 172.19.0.6 — un simple conteneur nginx servant une page avec un contenu d'apparence interne réaliste (titre « Internal Ops Console », une valeur en forme de jeton de session).
  2. Référence — confirmer que le filtre fonctionne normalement :
    root@kitploit:~
    curl -s -X POST http://127.0.0.1:3000/v1/scrape -H "Content-Type: application/json" \
      -d '{"url":"http://172.19.0.6/","renderJs":false}'
    → {"success":false,"error":"Invalid request: Access to 172.19.0.6 is not allowed", ...}
    
    curl -s -X POST http://127.0.0.1:3000/v1/scrape -H "Content-Type: application/json" \
      -d '{"url":"https://httpbin.org/redirect-to?url=http://172.19.0.6/&status_code=302","renderJs":false}'
    → {"success":false,"error":"HTTP request failed: error following redirect ...", ...}
    
    (capture d'écran : evidence/ssrf_baseline_blocked.png)
  3. Contournement — même redirection, niveau de rendu JS demandé :
    root@kitploit:~
    curl -s -X POST http://127.0.0.1:3000/v1/scrape -H "Content-Type: application/json" \
      -d '{"url":"https://httpbin.org/redirect-to?url=http://172.19.0.6/&status_code=302","renderJs":true,"renderer":"chrome","formats":["markdown"]}'
    
    Résultat : HTTP 200, "success":true, "data":{"markdown":"# Internal Ops Console\n\n# internal-ops.crw-lab.local\n\nNode role: primary\n\nBuild: 2026.08.02-rc3\n\nSession token: 8f3d1c9a7e2b4460b9e5d6a1c0f7e3d2", ..., "warnings":["redirected_to: http://172.19.0.6/"], "renderDecision":{"kind":"userPinned","renderer":"chrome"}} — le contenu du serveur interne est renvoyé à l'appelant ; l'avertissement redirected_to de l'outil lui-même montre qu'il sait qu'il a suivi la requête vers l'adresse bloquée et renvoie le contenu quand même. (capture d'écran : evidence/ssrf_chrome_tier_bypass.png)
  4. Confirmé que le contournement se déclenche également sur la chaîne de repli par défaut sans épinglage explicite de moteur de rendu ("renderJs":true seul — la manière ordinaire dont un appelant demande le rendu JS) : l'échelle a choisi lightpanda ("renderDecision":{"kind":"autoDefault","chosen":"lightpanda"}, "renderedWith":"lightpanda") et a renvoyé le même contenu interne, prouvant que les deux niveaux CDP (pas seulement chrome) partagent la faille.

https://httpbin.org/redirect-to représente « toute URL accessible en externe qui renvoie un 302 » ; un véritable attaquant hébergerait cela sur son propre domaine. 172.19.0.6 représente toute adresse dans les plages que url_safety est conçu pour bloquer (RFC1918, loopback, link-local/169.254.169.254 métadonnées cloud, .internal) — le contournement se produit avant que toute vérification de plage ne s'exécute (le chemin CDP n'appelle jamais url_safety du tout), il n'est donc pas spécifique à cette seule plage bloquée. Un point de terminaison de métadonnées cloud en direct n'était pas disponible dans ce laboratoire local, donc cette cible spécifique n'a pas été directement exercée ; il s'agit d'une extrapolation directe, tracée dans le code, et non d'une affirmation non testée.

Impact

Tout appelant capable d'atteindre /v1/scrape, /v2/scrape, /v1/crawl, /v1/map, /v1/extract, leurs équivalents batch/v2, ou les outils MCP équivalents — avec renderJs:true — peut utiliser le déploiement crw cible comme un proxy ouvert vers le réseau interne : lire les réponses des services loopback du déployeur, des services internes adressés en RFC1918 (bases de données, panneaux d'administration, API internes), et — sur tout déploiement hébergé dans le cloud — le point de terminaison de métadonnées cloud de l'instance (169.254.169.254), récupérant potentiellement les identifiants IAM/instance. Sur le déploiement auto-hébergé par défaut livré (aucune clé API configurée), cela ne nécessite aucune authentification du tout.

Faiblesses

  • CWE-918 : Server-Side Request Forgery (SSRF)
  • CWE-441 : Proxy ou intermédiaire involontaire (« Confused Deputy »)

Remédiation

La pompe d'interception Fetch.requestPaused déjà câblée dans les niveaux CDP (crates/crw-renderer/src/cdp.rs, pilotée par Blocklist dans blocklist.rs) est le point d'étranglement naturel : l'étendre (ou ajouter une vérification sœur invoquée depuis la même pompe, pour les backends chrome et lightpanda) pour appeler crw_core::url_safety::validate_safe_url contre l'URL de chaque requête interceptée — couvrant la navigation initiale, chaque saut de redirection, chaque navigation pilotée par JS, et chaque chargement XHR/fetch/iframe dans la même page — et Fetch.failRequest tout ce qui se résout vers un hôte bloqué, correspondant à ce que safe_redirect_policy() fait déjà pour le niveau http_only. Notez que cela signifie que Fetch.enable/l'interception ne peut plus rester conditionnellement désactivé lorsque la protection SSRF dépend de son exécution. En défense en profondeur, transformez également la vérification existante du final_url post-navigation dans crates/crw-crawl/src/single.rs:559-569 d'une entrée cosmétique warnings en un échec dur via url_safety::validate_safe_url_resolved, pour rattraper ce qui échappe à l'interception (par exemple une toute première réponse qui court-circuite la configuration de Fetch.enable).

Crédit

Dostxodjayev Abdullox

Canal de signalement

.github/SECURITY.md (rendu sur https://github.com/us/crw/security) : GitHub Private Vulnerability Reporting est le canal préféré (« ouvrir un rapport depuis l'onglet Security de ce dépôt »), avec [email protected] comme solution de repli par e-mail. Confirmé réellement actif et ouvert pour ce dépôt : gh api repos/us/crw/private-vulnerability-reporting --jq .enabled → true, et un bouton « Report a vulnerability » actif a été observé sur https://github.com/us/crw/security (renvoyant vers https://github.com/us/crw/security/advisories/new). La déclaration de périmètre sur cette page inclut explicitement « The crw-server binary and all workspace crates in this repository » et « The MCP server (crw-mcp) » ; elle exclut « Third-party headless browsers (Chromium, Lightpanda) invoked by the renderer » — cette découverte concerne la validation manquante de crw lui-même autour de l'appel de navigation CDP, et non un bug dans Chromium/Lightpanda lui-même, elle est donc dans le périmètre.

Télécharger l’outil