
Il filtro SSRF controllava il testo dell'hostname, ma la destinazione effettiva veniva decisa successivamente dal DNS. Questo gap permetteva agli URL Webhook controllati dall'attaccante di raggiungere destinazioni di loopback, metadata e rete privata.
Il filtro SSRF controllava il testo del nome host, ma la destinazione effettiva veniva decisa successivamente dal DNS. Questa lacuna permetteva agli URL Webhook controllati dall'attaccante di raggiungere destinazioni loopback, metadata e rete privata.
Ho trovato questo problema mentre esaminavo Typebot, un costruttore di chatbot open-source, con una semplice domanda di sicurezza in mente:
Cosa succede se la protezione SSRF convalida il testo del nome host, ma la destinazione reale viene decisa successivamente dal DNS?
In questo caso, quella domanda ha portato a un bug reale.
La protezione SSRF di Typebot per i blocchi Webhook / Richiesta HTTP convalidava solo:
Non risolveva i nomi host prima di autorizzare la richiesta.
Ciò significava che un nome host come ssrf-repro.example poteva sembrare innocuo durante la convalida, per poi risolversi in:
127.0.0.1169.254.169.254ed essere comunque recuperato dal client HTTP backend.
Questo problema è diventato CVE-2026-34207.
Typebot: Typebot su GitHub
CVE: CVE-2026-34207
Corretto in: 3.16.0
Questo ha interessato Typebot, una piattaforma chatbot open-source ampiamente utilizzata. Sul suo sito ufficiale, Typebot si presenta come affidabile per oltre 650 aziende in tutto il mondo. Il sito pubblicizza anche oltre 2 milioni di chat mensili e oltre 1,5 milioni di bot pubblicati.
URL Webhook controllato dall'attaccante -> il nome host supera la convalida SSRF solo letterale -> nessuna risoluzione DNS prima della decisione di autorizzazione -> il client HTTP backend risolve il nome host in una destinazione interna -> la richiesta lato server raggiunge loopback / metadata / rete privata -> i dati della risposta diventano disponibili tramite i log di esecuzione
Typebot è un costruttore di chatbot.
Permette agli utenti di creare flussi che possono:
Webhook / Richiesta HTTPCiò significa che l'esecuzione di richieste in uscita è un vero e proprio confine di sicurezza.
La domanda importante qui non era se Typebot supportasse i blocchi Webhook.
La vera domanda era:
La protezione SSRF convalida la destinazione effettiva a cui il server si collegherà, o solo il testo del nome host che appare nell'URL?
In questo caso, convalidava solo prima la forma testuale.
Questo è stato l'errore.
Le difese SSRF falliscono in modi molto prevedibili.
La maggior parte delle volte, gli errori interessanti non sono:
169.254.169.254"localhost"Gli errori più forti sono errori di confine:
Qui era il posto giusto dove guardare.
Typebot aveva già una logica di hardening SSRF per IP metadata letterali, loopback, intervalli privati e trucchi IP codificati.
Questo rendeva ovvia la domanda successiva:
Cosa succede se il nome host non è letteralmente pericoloso, ma si risolve in una destinazione pericolosa successivamente?
Questo è esattamente ciò che è successo.
Il problema principale era la convalida della destinazione basata sul testo del nome host invece che sull'indirizzo IP risolto.
Nell'implementazione vulnerabile, validateHttpReqUrl():
http: e https:metadata.google.internal, metadata.goog, metadata e localhostQuesta è la parte importante.
Se il nome host era un valore normale come:
ssrf-repro.example
allora parseIPAddress(hostname) restituiva null, e il validatore si fermava lì.
Non avveniva alcuna risoluzione DNS prima dell'approvazione.
Quindi la logica vulnerabile si riduceva effettivamente a:
const ip = parseIPAddress(hostname);
if (ip) {
validateIPAddress(ip);
}
Ciò significa che:
La seconda metà del bug era nel percorso di esecuzione.
In executeHttpRequest(), Typebot eseguiva prima la convalida e poi successivamente effettuava la richiesta effettiva con ky(request.url, ...).
Quindi la sequenza era:
Questa è l'intera vulnerabilità.
La distinzione importante è dove è stata presa la decisione di fiducia.
Molti bug sembrano piccoli se li descrivi male.
Se descrivi questo come:
"il filtro del nome host era incompleto"
sembra un problema di qualità.
Questo non è il vero problema.
Il vero problema era:
Questa non è una debolezza di filtraggio estetica.
È un fallimento del confine di fiducia.
E poiché l'esecutore HTTP registrava i dati di risposta nei log di esecuzione, il problema non era nemmeno cieco nei casi più forti.
Quindi non era solo:
Era:
Questa è una vera vulnerabilità SSRF.
Ho usato due livelli di prova perché dimostravano due cose diverse.
La prima PoC isolava pulitamente la causa principale.
Ho usato un piccolo harness locale che:
127.0.0.1http://ssrf-repro.example:18080/...ssrf-repro.example si risolvesse in 127.0.0.1Questo dimostrava l'esatto difetto:
L'output catturato mostrava:
Questo dimostrava direttamente la lacuna di convalida.
La seconda PoC mostrava il bug attraverso il percorso di funzionalità effettivo che conta.
La riproduzione più semplice era:
Webhook che punta a quel nome host benignoUn blocco rappresentativo appariva così:
{
"id": "blk-webhook",
"type": "Webhook",
"options": {
"webhook": {
"method": "GET",
"url": "http://ssrf-repro.example:8000/"
}
}
}
Con una voce nel file hosts come:
127.0.0.1 ssrf-repro.example
il validatore accettava comunque l'URL perché:
ssrf-repro.example non era in blockedHostnameslocalhostparseIPAddress("ssrf-repro.example") restituiva nullPoi il client HTTP backend risolveva il nome host in 127.0.0.1 e si collegava comunque.
Questo stabiliva l'intera affermazione:
La prima PoC dimostra la causa principale.
La seconda PoC dimostra l'impatto sul prodotto.
Questa divisione è importante.
Se mostri solo:
"questo filtro accetta un nome host"
non hai mostrato abbastanza.
Se mostri solo:
"si è verificata una richiesta interna"
non hai isolato il perché.
Il report più forte è:
Questa è la storia completa.
Questo problema è stato ragionevolmente classificato come Alto.
La classificazione dell'avviso era:
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:L/A:L
Ha senso.
Non era di per sé un SSRF non autenticato su scala internet.
I privilegi richiesti erano Bassi perché il bug standalone richiedeva un attore che potesse configurare o attivare un blocco Webhook / Richiesta HTTP.
Ma all'interno di quel confine, l'impatto era serio:
Questo è un forte problema SSRF da solo.
E diventa ancora più pericoloso se combinato con altri bug che ampliano la raggiungibilità.
Alcune persone vedono un SSRF autenticato e lo sottovalutano immediatamente.
Questo è un errore.
La vera domanda non è:
"L'attaccante era loggato?"
La vera domanda è:
"Poteva quell'attaccante far connettere il server a un luogo che il modello di sicurezza avrebbe dovuto proibire?"
Qui, la risposta era sì.
Il validatore affermava di proteggere:
Ma una lacuna nella risoluzione del nome host permetteva a quelle stesse destinazioni di rientrare attraverso una rappresentazione diversa.
Questo è esattamente il tipo di bug che merita un CVE.
La correzione era solida perché correggeva il confine reale, non solo un sintomo.
Nell'implementazione corretta, validateHttpReqUrl() è stata modificata per risolvere i nomi host prima di approvare la richiesta.
Il validatore ora:
lookup da node:dns/promisesQuesta è la correzione giusta perché cambia la decisione di fiducia da:
a:
Questa è la proprietà di sicurezza che avrebbe dovuto esistere dall'inizio.
La remediation ha anche aggiunto copertura per regressioni su:
Questo è il tipo di hardening che vuoi in una vera correzione SSRF:
Il problema è stato risolto in Typebot 3.16.0.
Questo problema è stato segnalato privatamente tramite il flusso di segnalazione di sicurezza di GitHub.
Il report includeva:
validateHttpReqUrl()executeHttpRequest()I manutentori hanno accettato il problema, corretto la logica di convalida, e la vulnerabilità è stata successivamente pubblicata come:
CVE-2026-34207
con la correzione rilasciata in Typebot 3.16.0.
La lezione chiave qui è semplice:
le decisioni di sicurezza devono essere prese sulla stessa rappresentazione che lo stack di rete utilizzerà effettivamente.
Sembra ovvio.
Ma molte protezioni SSRF falliscono proprio perché non seguono questa regola.
Questo è sufficiente.
Questo bug rafforza anche qualcosa di importante sulla revisione SSRF in generale:
Se non risolvi e convalidi la destinazione reale, il tuo filtro SSRF è ancora incompleto.
Questo è il vero insegnamento.
Questa vulnerabilità non riguardava un payload fantasioso.
Riguardava il porsi la giusta domanda sul confine.
In Typebot, il validatore SSRF controllava prima il testo del nome host. La destinazione effettiva veniva decisa successivamente dal DNS. Il client di rete seguiva l'indirizzo risolto.
Quella lacuna era il bug.
Ecco perché questo è diventato CVE-2026-34207.
Corretto in Typebot 3.16.0.