KIberschutz: Sechs Lücken in hopp, Fix an Tag 1

Wir meldeten dem Pair-Programming-Tool hopp sechs Funde, darunter einen kritischen. Einen Tag später war alles behoben, nach fünf Tagen ausgeliefert.

9 Min. Lesezeit

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 und itflow ist das der fünfte Fall. Und der schnellste, den wir bisher erlebt haben.

Wir haben am 29. Juli sechs Funde gemeldet. Am 30. Juli waren alle sechs behoben, gemergt und im Hauptzweig. In unseren bisherigen Fällen hat allein die erste Rückmeldung länger gedauert. Das Release folgte am 3. August.

Um welche Software es geht

hopp ist ein Open-Source-Werkzeug für Remote-Pair-Programming, eine Alternative zu Tuple. Zwei Leute teilen einen Bildschirm, und der Partner kann Tastatur und Maus der anderen Maschine übernehmen. Gebaut mit Tauri und Rust, das Backend in Go, die Übertragung läuft über LiveKit. AGPL-3.0, 671 Sterne auf GitHub. Es gibt ein bezahltes Cloud-Angebot und eine Self-Hosting-Variante.

Das Nutzungsmuster ist der Grund, warum wir uns solche Werkzeuge ansehen. Wer in einem hopp-Raum sitzt, hat Zugriff auf die Tastatur eines fremden Entwicklungsrechners. Die Frage, wer überhaupt in einen Raum darf, ist hier keine Formalie, sondern die eigentliche Sicherheitsgrenze. Genau dort lagen unsere schwersten Funde.

Der Scan blieb bewusst schmal: nur das Go-Backend, 6.305 Zeilen in 30 Dateien. Rust-Core, Desktop-App und Web-App haben wir nicht angefasst. Ein Backend dieser Größe liest sich in einem Durchgang, und das ist bei Autorisierungslogik ein Vorteil, weil man alle Pfade nebeneinander legen kann.

Die Funde

FundSchwereKurz erklärt
Beim Registrieren ins fremde TeamkritischDas Anmeldeformular übernahm Felder aus der Anfrage, die es nie hätte übernehmen dürfen. Wer sich registrierte, konnte sich gleich als Administrator in ein beliebiges fremdes Team eintragen
Fremde Räume umbenennen und löschenhochDie Prüfung, ob ein Raum zum eigenen Team gehört, verglich zwei Werte, die an dieser Stelle immer leer waren. Der Vergleich schlug damit nie an, und jeder angemeldete Nutzer konnte fremde Räume löschen
Anrufe im Namen anderer annehmenhochBeim Annehmen eines Anrufs glaubte der Server der Angabe, wer angerufen hat, ohne zu prüfen, ob es diesen Anruf überhaupt gab. Er stellte daraufhin Zugangs-Tokens für die genannte Person aus
Phishing über die hopp-AbsenderdomainmittelNamen von Nutzern landeten ungefiltert in den HTML-Mails des Systems. Über die Team-Einladung ließ sich damit beliebiges HTML an beliebige Adressen schicken, versendet von der signierten Absenderdomain des Projekts
Leeres Sitzungsgeheimnis wurde akzeptiertmittelStartete jemand die Software ohne gesetztes Sitzungsgeheimnis, lief sie trotzdem, und alle Anmelde-Tokens waren mit einem leeren Schlüssel signiert und damit von jedem fälschbar. Die mitgelieferte Beispielkonfiguration hatte das Feld leer
Passwort-Reset-Mail landete auf der PlatteniedrigBei jedem Passwort-Reset schrieb der Server die fertige Mail samt gültigem Reset-Link in eine Datei im Arbeitsverzeichnis. Übrig gebliebener Debug-Code

Der kritische Fund ist der interessanteste, weil er so unscheinbar aussieht. Das Anmeldeformular las die Anfrage in eine Datenstruktur ein, die das komplette Nutzermodell enthielt. Damit kamen zwei Felder mit, die niemand von außen setzen können sollte: das Admin-Kennzeichen und die Team-Zugehörigkeit. Die Logik, die einem neuen Nutzer normalerweise ein eigenes Team gibt, sprang nur an, wenn kein Team mitgeschickt wurde. Wer eines mitschickte, übersprang sie. Und weil Team-Nummern schlicht durchgezählt werden, muss man nicht raten, sondern nur zählen.

Praktisch heißt das: Registrierung mit einem zusätzlichen Feld im Anfrage-Body, und man sitzt als Admin im Team einer fremden Firma. Für ein Werkzeug, dessen Zweck die Fernsteuerung fremder Rechner ist, ist das die denkbar unangenehmste Stelle. Wir haben das nicht nur im Code gelesen, sondern mit einem lauffähigen Nachweis gegen die projekteigene Testumgebung belegt.

Auffällig ist auch das Muster bei den beiden Raum-Funden. Die Nachbarfunktionen im selben Modul machten die Team-Prüfung korrekt, zwei Handler daneben nicht. Der Fehler war kein Denkfehler im Konzept, sondern ein Vergleich auf der falschen Ebene, der im Normalbetrieb nie auffällt, weil er einfach immer durchwinkt.

Was das Team daraus gemacht hat

Als wir mit dem Scan anfingen, war der private Meldeweg auf GitHub bei hopp abgeschaltet, und eine SECURITY.md gab es nicht. Für uns heißt das: erst fragen, wohin die Funde sollen, dann melden. Das Team hat den privaten Kanal daraufhin eingeschaltet, und wir konnten am 29. Juli fünf Advisories einreichen. Der sechste Fund, der Debug-Code mit der Reset-Mail, war ein lokales Problem ohne Angriffsweg über das Netz. Den haben wir bewusst als normalen öffentlichen Bug gemeldet.

Zu jedem der fünf Advisories haben wir einen Fix beigesteuert, über den dafür vorgesehenen Weg: GitHub legt zu einem privaten Advisory auf Knopfdruck einen temporären privaten Fork an, in dem man einen Branch bauen kann, ohne die Lücke vorzeitig sichtbar zu machen.

Verwendet hat der Maintainer sie nicht direkt. Er hat PR #371 aufgemacht, alle fünf Advisories daran gehängt und die Fixes selbst geschrieben, 166 hinzugefügte und 87 entfernte Zeilen in sechs Dateien. Am 30. Juli abends war der PR gemergt. Das ist völlig in Ordnung und aus unserer Sicht sogar das bessere Ergebnis: Wer den Fix selbst schreibt, hat verstanden, was schiefging. Ein durchgewunkener fremder Patch beweist das nicht.

Inhaltlich decken sich die Lösungen mit dem, was wir vorgeschlagen hatten. Das Anmeldeformular bekam eine eigene, enge Datenstruktur. Die Team-Prüfungen wanderten in eine gemeinsame Hilfsfunktion, die im Zweifel ablehnt statt durchzulassen. Das Annehmen eines Anrufs hängt jetzt an einem Marker, der beim Initiieren gesetzt wird. Die Mail-Templates laufen durch eine zentrale Funktion, die alle Platzhalter escaped. Und ohne gesetztes Sitzungsgeheimnis startet die Software nicht mehr.

Der Haken: davon erfährt kaum jemand

So gut die Reaktion war, an einer Stelle bleibt der Fall unbefriedigend, und das gehört hierher.

Die fünf Advisories sind nicht veröffentlicht worden, sondern geschlossen. Damit fehlt die öffentliche Sicherheitsmeldung, und mit ihr die CVE-Nummer und jeder Eintrag, den ein Abhängigkeits-Scanner aufgreifen könnte. In den Release Notes zu v1.0.25 steht unter den Bugfixes eine Zeile: "Vuln report fixes (#371)". Zwischen einem Fix für Audio-Ausgabe und einem für Codec-Probleme.

Wer hopp selbst betreibt und die Release Notes überfliegt, hat keine Chance zu erkennen, dass in dieser Version eine kritische Umgehung der Team-Grenze steckt. Er sieht einen Bugfix unter mehreren und verschiebt das Update vielleicht auf nächste Woche. Genau dafür sind veröffentlichte Advisories da: Sie sagen nicht nur, dass etwas gefixt wurde, sondern wie dringend das Update ist.

Wir wollen das nicht größer machen, als es ist. Ein zweiköpfiges Team, das binnen 24 Stunden sechs Funde abräumt, hat den schwierigeren Teil der Arbeit erledigt. Der Papierkram danach ist der leichtere, und er lässt sich nachholen: Ein Advisory kann auch Wochen später noch veröffentlicht werden. Sobald das passiert, tragen wir die Links hier nach.

Was ihr tun solltet

Wenn ihr hopp selbst hostet, ist das hier die kurze Fassung:

Aktualisiert auf mindestens v1.0.25. Alle sechs Fixes sind dort drin, das ist per Tag-Zugehörigkeit des Merge-Commits belegt. Ältere Versionen enthalten sämtliche beschriebenen Lücken.

Prüft danach, ob euer SESSION_SECRET tatsächlich gesetzt ist. Die ausgelieferte Beispielkonfiguration hatte das Feld leer, und bis v1.0.25 ist die Software damit klaglos gestartet. Wer sie in dem Zustand betrieben hat, sollte das Geheimnis jetzt neu setzen, was alle bestehenden Sitzungen ungültig macht. Das ist genau der gewünschte Effekt.

Und schaut nach, ob im Arbeitsverzeichnis des Backends eine Datei testing.html liegt. Falls ja, enthält sie die zuletzt verschickte Passwort-Reset-Mail inklusive Link. Löschen.

Für die Cloud-Variante von hopp müsst ihr nichts tun, die läuft auf der aktuellen Version.

Wie es weitergeht

Der Fall ist damit abgeschlossen und auf unserer Statusseite unter dem Klarnamen zu finden. Das ist die Regel, an die wir uns halten: Solange eine gemeldete Lücke offen ist, taucht ein Projekt bei uns nur anonym auf. Erst wenn alles behoben und ausgeliefert ist, nennen wir den Namen. Bei hopp hat das fünf Tage gedauert.

Andere Fälle liegen deutlich länger. Deshalb steht auf der Statusseite auch, was noch offen ist, ohne die betroffenen Projekte zu nennen.

Quellen4