
Der SSRF-Filter prüfte den Hostname-Text, aber das tatsächliche Ziel wurde später durch DNS bestimmt. Diese Lücke ermöglichte es angreifergesteuerten Webhook-URLs, Loopback-, Metadaten- und private Netzwerkziele zu erreichen.
Der SSRF-Filter überprüfte den Hostnamen-Text, aber das tatsächliche Ziel wurde später durch DNS bestimmt. Diese Lücke erlaubte es angreiferkontrollierten Webhook-URLs, Loopback-, Metadaten- und private Netzwerkziele zu erreichen.
Ich habe diesen Fehler gefunden, während ich Typebot, einen Open-Source-Chatbot-Builder, mit einer einfachen Sicherheitsfrage im Hinterkopf überprüft habe:
Was passiert, wenn der SSRF-Schutz den Hostnamen-Text validiert, das tatsächliche Ziel aber später durch DNS bestimmt wird?
In diesem Fall führte diese Frage zu einem echten Bug.
Der SSRF-Schutz von Typebot für Webhook / HTTP-Request-Blöcke validierte nur:
Er hat nicht Hostnamen aufgelöst, bevor er die Anfrage erlaubte.
Das bedeutete, dass ein Hostname wie ssrf-repro.example während der Validierung harmlos aussehen konnte, sich dann aber auflösen konnte in:
127.0.0.1169.254.169.254und trotzdem vom Backend-HTTP-Client abgerufen wurde.
Dieser Fehler wurde CVE-2026-34207.
Typebot: Typebot auf GitHub
CVE: CVE-2026-34207
Behoben in: 3.16.0
Dies betraf Typebot, eine weit verbreitete Open-Source-Chatbot-Plattform. Auf der offiziellen Website wird Typebot als vertrauenswürdig von 650+ Unternehmen weltweit präsentiert. Die Website wirbt außerdem mit 2M+ monatlichen Chats und 1.5M+ veröffentlichten Bots.
angreiferkontrollierte Webhook-URL -> Hostname passiert nur literale SSRF-Validierung -> keine DNS-Auflösung vor der Entscheidung -> Backend-HTTP-Client löst Hostname zu internem Ziel auf -> serverseitige Anfrage erreicht Loopback / Metadaten / privates Netzwerk -> Antwortdaten werden durch Ausführungsprotokolle verfügbar
Typebot ist ein Chatbot-Builder.
Es ermöglicht Benutzern, Abläufe zu erstellen, die:
Webhook / HTTP-Request-Blöcke auslösenDas bedeutet, dass die Ausführung ausgehender Anfragen eine echte Sicherheitsgrenze darstellt.
Die wichtige Frage war hier nicht, ob Typebot Webhook-Blöcke unterstützt.
Die eigentliche Frage war:
Validiert der SSRF-Schutz das tatsächliche Ziel, mit dem der Server eine Verbindung herstellt, oder nur den Hostnamen-Text, der in der URL erscheint?
In diesem Fall validierte er zuerst nur die Textform.
Das war der Fehler.
SSRF-Abwehrmechanismen versagen auf sehr vorhersehbare Weise.
Meistens sind die interessanten Fehler nicht:
169.254.169.254 zu blockieren"localhost zu blockieren"Die stärkeren Fehler sind Grenzfehler:
Das war hier der richtige Ansatzpunkt.
Typebot hatte bereits SSRF-Härtungslogik für literale Metadaten-IPs, Loopback, private Bereiche und codierte IP-Tricks.
Das machte die nächste Frage offensichtlich:
Was ist, wenn der Hostname wörtlich nicht gefährlich ist, sich aber später in ein gefährliches Ziel auflöst?
Genau das ist passiert.
Die Grundursache war die Zielvalidierung basierend auf Hostnamen-Text anstatt der aufgelösten IP-Adresse.
In der anfälligen Implementierung hat validateHttpReqUrl():
http: und https: erlaubtmetadata.google.internal, metadata.goog, metadata und localhost blockiertDas ist der wichtige Teil.
Wenn der Hostname ein normaler Wert war wie:
ssrf-repro.example
dann gab parseIPAddress(hostname) null zurück, und der Validator hörte dort auf.
Es fand keine DNS-Auflösung vor der Genehmigung statt.
Die anfällige Logik reduzierte sich also effektiv auf:
const ip = parseIPAddress(hostname);
if (ip) {
validateIPAddress(ip);
}
Das bedeutet:
Die zweite Hälfte des Bugs lag im Ausführungspfad.
In executeHttpRequest() führte Typebot zuerst die Validierung durch und später die eigentliche Anfrage mit ky(request.url, ...).
Die Reihenfolge war also:
Das ist die gesamte Schwachstelle.
Der wichtige Unterschied ist wo die Vertrauensentscheidung getroffen wurde.
Viele Bugs wirken klein, wenn man sie schlecht beschreibt.
Wenn man diesen Bug beschreibt als:
"Der Hostname-Filter war unvollständig"
klingt das nach einem Qualitätsproblem.
Das ist nicht das eigentliche Problem.
Das eigentliche Problem war:
Das ist keine kosmetische Filtersschwäche.
Das ist ein Vertrauensgrenzenfehler.
Und weil der HTTP-Ausführer Antwortdaten in Ausführungsprotokollen aufzeichnete, war das Problem nicht einmal in den stärksten Fällen blind.
Es war also nicht nur:
Es war:
Das ist eine echte SSRF-Schwachstelle.
Ich habe zwei Proof-Ebenen verwendet, weil sie zwei verschiedene Dinge demonstrierten.
Der erste PoC isolierte die Grundursache sauber.
Ich verwendete eine kleine lokale Testumgebung, die:
127.0.0.1 startetehttp://ssrf-repro.example:18080/... validiertessrf-repro.example zu 127.0.0.1 aufgelöst wurdeDas demonstrierte den genauen Fehler:
Die erfasste Ausgabe zeigte:
Das bewies die Validierungslücke direkt.
Der zweite PoC zeigte den Bug durch den tatsächlichen Feature-Pfad, der relevant ist.
Die einfachste Reproduktion war:
Webhook-Block, der auf diesen harmlosen Hostnamen zeigtEin repräsentativer Block sah so aus: