KIberschutz: Sieben Lücken in chattermate
Wir meldeten dem Support-Chatbot chattermate sieben Funde. Alle sind behoben und ausgeliefert. Nur bei den Fix-Versionen sind wir uns mit dem Team uneinig.
Transparenz vorweg: KIberschutz ist unser eigenes Projekt. Wir prüfen mit KI-Unterstützung Open-Source-Software auf Sicherheitslücken und melden die Funde verantwortungsvoll an die Maintainer. Nach logtide, Speakr, Mira, itflow, hopp und c4 ist chattermate der nächste Fall. Die Fixes waren schnell da. Interessant ist, was danach kam.
Wir haben am 24. Juli sieben Funde gemeldet. Einen Tag später hing der Sammel-Fix im Hauptzweig, drei Tage später im Release. Am 10. September hat das Team fünf der Advisories öffentlich gemacht. Und genau dabei ist uns etwas aufgefallen: Bei den Versionsangaben, die sagen sollen "ab hier bist du sicher", gehen unsere Analyse und die des Teams an drei Stellen auseinander. Einmal zu euren Ungunsten, einmal zu unseren, einmal ohne echte Folgen.
Um welche Software es geht
chattermate ist eine Open-Source-Plattform für KI-Kundensupport, eine selbst hostbare Alternative zu Intercom oder Zendesk. Ein Chat-Widget auf der Website, ein KI-Agent, der Standardfragen beantwortet, und die Übergabe an einen menschlichen Mitarbeiter, wenn es komplex wird. Backend in Python, Apache-2.0, mit einer nativen Shopify-Anbindung für Onlineshops. Es gibt einen kostenlosen gehosteten Dienst und die Self-Hosting-Variante.
Für uns ist so ein Werkzeug aus zwei Gründen interessant. Es steht am Kundenkontakt und verarbeitet echte Support-Verläufe, also personenbezogene Daten. Und es klemmt zwischen mehreren fremden Systemen: Shopify, SMS-Anbieter, das eingebettete Widget auf beliebigen Websites. Jede dieser Nahtstellen ist eine Stelle, an der Vertrauen falsch gesetzt werden kann.
Die sieben Funde
| Fund | Schwere | Kurz erklärt |
|---|---|---|
| Shopify-Secret über einen offenen Endpunkt | kritisch | Ein Endpunkt für den Shopify-Token-Tausch prüfte das mitgeschickte Token nicht und rief eine im Token frei wählbare Adresse serverseitig auf. Wer eine eigene Adresse eintrug, bekam das Shopify-API-Secret des Betreibers zugeschickt. Ohne Anmeldung |
| Echter Schlüssel in der Beispielkonfiguration | hoch | Die mitgelieferte Beispielkonfiguration enthielt einen echten, funktionierenden Verschlüsselungsschlüssel statt eines Platzhalters, und die Anleitung sagte nur "kopieren". Wer ihr folgte, verschlüsselte alle gespeicherten Zugangsdaten mit einem Schlüssel, der öffentlich im Projekt steht. Die Anmelde-Tokens hingen an einem ebenso bekannten Wert |
| SQL-Injection über einen Titel | hoch | Der Titel einer Wissensquelle wurde später per Textverkettung in eine Datenbankabfrage eingesetzt. Ein Anführungszeichen im Titel brach aus der Abfrage aus. Ein Kommentar im Code behauptete, das sei sicher, weil der Wert als JSON abgelegt werde. Das stimmt nicht, JSON schützt keine Anführungszeichen |
| Server holt beliebige URLs ab | hoch | Der Web-Crawler war gegen interne Adressen abgesichert, sein Browser-Fallback aber nicht. Der lud jede Adresse mit einem echten Browser, auch interne Dienste und Cloud-Metadaten. Einer der Einstiege war ohne Anmeldung erreichbar |
| SMS-Webhooks ohne Echtheitsprüfung | hoch | Eingehende SMS-Benachrichtigungen wurden nicht auf Echtheit geprüft, die zuständige Funktion gab immer "in Ordnung" zurück. Damit ließen sich SMS fälschen und Ausgangs-SMS auf Kosten des Betreibers auslösen |
| Widget-Schlüssel liest fremde Kundendaten | mittel | Ein Widget-Schlüssel, der sichtbar im Einbettungs-Code jeder Website steckt, konnte die E-Mail-Adressen und aktiven Sitzungen aller Kunden der Organisation abfragen und Sitzungen widerrufen. Zwei eng verwandte Endpunkte, in einem Advisory zusammengefasst |
Der erste Fund ist der schwerste, und er zeigt gut, wie so etwas entsteht. Der Endpunkt für den Shopify-Token-Tausch nahm ein mitgeschicktes Token entgegen und las daraus ein Feld aus, das angibt, an welchen Shop die Anfrage gehen soll. Nur wurde die Signatur des Tokens nie geprüft. Ein Token kann sich also jeder selbst bauen. Aus dem enthaltenen Zielfeld baute der Server eine Adresse und schickte dorthin die Zugangsdaten der Shopify-App, darunter das geheime App-Secret. Trug ein Angreifer als Ziel seine eigene Adresse ein, bekam er das Secret frei Haus. Der Endpunkt verlangte keine Anmeldung.
Zwei fehlende Prüfungen an einer Stelle, und aus einer Komfortfunktion wird ein Weg, das zentrale Geheimnis der Shopify-Anbindung abzugreifen. Für sich genommen sieht keine der beiden Auslassungen dramatisch aus. Gefährlich wird erst, dass sie am selben Endpunkt zusammentreffen und ihn ohne Anmeldung erreichbar lassen.
Was das Team daraus gemacht hat
Der private Meldeweg auf GitHub war bei chattermate aktiv, wir konnten die Funde also direkt als Advisories einreichen. Zu fünf davon haben wir einen Fix beigesteuert, über den dafür vorgesehenen Weg mit einem temporären privaten Fork. Bei zwei Funden haben wir bewusst keinen fertigen Patch geliefert, sondern nur die Richtung beschrieben, weil sie am Design des Onboardings hängen.
Verwendet hat der Maintainer die Patches nicht eins zu eins. Er hat Pull Request #250 aufgemacht und die Fixes selbst geschrieben, gemergt am 25. Juli, einen Tag nach der Meldung. Das ist aus unserer Sicht das bessere Ergebnis. Wer den Fix selbst schreibt, hat verstanden, was schiefging. Inhaltlich decken sich die Lösungen mit unseren Vorschlägen: Das Token wird jetzt verifiziert, die Suche arbeitet mit Datenbank-Parametern statt zusammengebautem Text, der Browser-Fallback bekam die Adressprüfung, und der SMS-Webhook verlangt jetzt ein Token.
Wo unsere Analyse und die des Teams auseinandergehen
Als das Team am 10. September fünf Advisories veröffentlichte, standen darin auch die gepatchten Versionen. Diese Angabe ist die wichtigste für jeden, der die Software selbst betreibt, denn sie sagt: Ab hier bist du sicher. Wir haben sie gegen den Code an den jeweiligen Versionsständen geprüft, und an drei Stellen weicht sie von dem ab, was wir erwartet hätten.
Das ist keine Erbsenzählerei. Ein Maintainer trägt diese Versionen von Hand ein, oft aus der Erinnerung. Ein Fehler in eine Richtung ist harmlos, in die andere gefährlich. Drei Funde, drei unterschiedliche Ausgänge.
Beim SSRF über den Crawler liegt das Advisory falsch, und zwar gefährlich. Es nennt v1.0.14 als gepatchte Version. Die Absicherung des Browser-Fallbacks kam aber nachweislich erst mit dem Fix vom 25. Juli, und der steckt in v1.0.17. In v1.0.14 bis v1.0.16 fehlt sie komplett, die betroffene Datei enthält dort keine einzige Zeile Schutzcode. Wer auf v1.0.14 sitzt, liest im Advisory "gepatcht" und ist es nicht. Das ist die eine Angabe, bei der man sich in falscher Sicherheit wiegen kann.
Bei den schwachen Default-Secrets waren wir selbst zu optimistisch. Wir hatten den Fund als mit v1.0.17 erledigt geführt, das Advisory nennt v1.0.18. Hier hat das Team recht. Der Sammel-Fix in v1.0.17 hat nur die Beispielkonfiguration angefasst. Der eigentliche Kern unseres Funds blieb: Der echte Schlüssel stand weiter als Vorgabewert im Code, und es gab keine Startsperre. Genau die kam erst in v1.0.18 am 31. Juli. Seitdem verweigert die Software den Start, wenn sie noch mit den öffentlich bekannten Schlüsseln läuft. Das ist die richtige Lösung, und sie kam eine Version später als wir dachten.
Bei der SQL-Injection stimmen beide, aber aus unterschiedlichen Gründen. Die Injection selbst war ab v1.0.17 geschlossen, die Textverkettung war weg. Der Fix führte allerdings einen Folgefehler ein, durch den das Verknüpfen von Wissensquellen abstürzte. Repariert wurde das erst in v1.0.18. Die Lücke war also ab v1.0.17 zu, die Funktion erst ab v1.0.18 wieder heil. Dass das Advisory v1.0.18 nennt, ist die vorsichtigere und damit die bessere Angabe.
Unterm Strich zeigt der Fall etwas, das über chattermate hinausgeht: Die Versionsangabe in einem Advisory ist eine Aussage eines Menschen, kein automatisch geprüfter Wert. Man sollte ihr nicht blind vertrauen, in keine der beiden Richtungen.
Was ihr tun solltet
Wenn ihr chattermate selbst hostet, ist das hier die kurze Fassung.
Aktualisiert auf mindestens v1.0.18, nicht nur auf v1.0.17. v1.0.17 schließt fünf der sieben Funde, aber die Startsperre gegen die öffentlichen Schlüssel und die Reparatur der Wissensverknüpfung kommen erst mit v1.0.18. Die im Advisory zum Crawler genannte v1.0.14 ist zu niedrig, verlasst euch nicht darauf.
Setzt danach eigene Schlüssel, falls noch nicht geschehen. Wer der alten Anleitung mit "Beispielkonfiguration kopieren" gefolgt ist, hat seine gespeicherten Zugangsdaten unter einem Schlüssel verschlüsselt, der öffentlich im Projekt steht, und signiert Anmelde-Tokens mit einem bekannten Wert. Ab v1.0.18 lässt euch die Software das nicht mehr unbemerkt tun, sie startet dann nicht. Wer vorher in diesem Zustand lief, sollte den Verschlüsselungsschlüssel und die Anmelde-Geheimnisse neu setzen. Das macht bestehende Sitzungen ungültig, was hier der gewünschte Effekt ist.
Und wenn ihr die Shopify-Anbindung nutzt: Das App-Secret war über den offenen Endpunkt abgreifbar. Wer die betroffenen Versionen im Einsatz hatte, sollte das Shopify-App-Secret rotieren.
Wie es weitergeht
Der Fall steht auf unserer Statusseite unter dem Klarnamen, weil sieben Funde behoben und ausgeliefert sind, fünf davon inzwischen auch als öffentliches Advisory dokumentiert. Er steht dort aber weiter als offen. Aus demselben Lauf ist noch ein Fund in Bearbeitung, und solange das so ist, nennen wir dazu keine Details. Das ist die Regel, an die wir uns halten: Ein einzelner offener Fund bleibt so lange ohne Beschreibung, bis sein Fix bei den Nutzern angekommen ist.
Die fünf veröffentlichten Advisories tragen bisher keine CVE-Nummer, das gemeinsame Advisory zu den beiden Widget-Funden ist noch nicht öffentlich. Sobald sich daran etwas tut oder der letzte Fund ausgeliefert ist, tragen wir es hier nach.
Quellen10
- GHSA-x8jh-4v23-8w6j: Shopify-Secret über den Token-Endpunktgithub.com
- GHSA-q76x-jwvg-4q2f: schwache Default-Secretsgithub.com
- GHSA-44g4-6p63-4frw: SQL-Injection über den Wissensquellen-Titelgithub.com
- GHSA-jjq4-m2w3-9rr2: SSRF über den Browser-Fallback des Crawlersgithub.com
- GHSA-gg5v-58wg-wp4j: SMS-Webhooks ohne Echtheitsprüfunggithub.com
- Pull Request #250: Fix reported security advisoriesgithub.com
- Release v1.0.17 (Sammelfix)github.com
- Release v1.0.18 (Startschutz für Secrets)github.com
- chattermate auf GitHubgithub.com
- KIberschutz: Status aller Fällekiberblick.de