host-unlimited.de: Ein Blick ins Webinterface

Auf der Suche nach einem geeigneten Hoster bin ich auf host-unlimited.de gestoßen. Das zunächst recht solid wirkende Webinterface offenbarte recht schnell ein paar Schwächen.

GraphQL braucht Know-how

Was mich sehr überrascht hat, war die Tatsache, dass hier fast ausschließlich GraphQL zum Einsatz kommt. In der Vergangenheit musste ich bereits mehrmals feststellen, dass GraphQL-Anwendungen teils gravierende Sicherheitsmängel aufweisen.

Unglückliches UI-Design

UI-Design kann schwierig sein. Vor allem wenn die Backend-Entwickler andere Pläne verfolgen. Folgendes kann sich daraus ergeben:

Hier sehen wird einen Screenshot aus den Benutzereinstellungen. Genauer gesagt sehen wir hier den Dialog zum Ändern des Passworts. Nun ist es eigentlich kein Geheimtipp, dass man beim Ändern des Passworts zunächst das aktuelle Passwort abfragen sollte. Das wurde hier scheinbar vergessen. Erstmal aber kein großes Problem. Es folgt ein Blick auf den HTTP-Post-Request zum Ändern des Passworts.

Was wir hier sehen ist ein Passwort, welches gesetzt wird. Was wir jedoch nicht sehen, ist ein CSRF-Token.

Let’s Hack

Zunächst muss man aber noch erwähnen, dass für CSRF bekanntlich ein Cross-Origin-Request abgesetzt werden muss. Hier gibt es einige Beschränkungen. Darunter fällt unter anderem die Regel, dass der Content-Type-Header nicht beliebig gesetzt werden kann. Die hier verwendete GraphQL-Schnittstelle verwendet den Content-Type application/json, welcher sich per Cross-Origin-Request nicht setzen lässt.

GraphQL ist bekanntlich ein sehr flexibles Framework und kommt auch ohne Probleme mit anderen Notationen klar. Für mich bestand nun die Herausforderung, die application/json-Notation in eine application/x-www-form-urlencoded zu übersetzen.

Aus

[{"operationName":"userEditSelfGeneral","variables":{"new_password":"Moin","nickname":"kd103663","forceEmailLogin":false,"localAvatarId":""},"query":"mutation userEditSelfGeneral($new_password: String, $nickname: String!, $forceEmailLogin: Boolean, $localAvatarId: ID) {\n userEditSelfGeneral(new_password: $new_password, nickname: $nickname, forceEmailLogin: $forceEmailLogin, localAvatarId: $localAvatarId) {\n …UserPart\n address {\n …UserPartAddress\n __typename\n }\n __typename\n }\n}\n\nfragment UserPartAddress on UserAddress {\n firstname\n lastname\n street1\n zip\n country\n title\n city\n birthdate\n phone\n phoneMobile\n company\n vatId\n id\n verified\n vat\n verifyDocument {\n id\n originalname\n url\n create\n __typename\n }\n __typename\n}\n\nfragment UserPart on User {\n avatar\n nickname\n credits\n email\n active\n supportId\n localAvatar {\n id\n url\n __typename\n }\n id\n forceEmailLogin\n __typename\n}\n"}]

wurde dann:

query=mutation userEditSelfGeneral($new_password: String, $nickname: String!, $forceEmailLogin: Boolean, $localAvatarId: ID) { userEditSelfGeneral(new_password: $new_password, nickname: $nickname, forceEmailLogin: $forceEmailLogin, localAvatarId: $localAvatarId) { address{__typename}}}
variables={"new_password":"Moin","nickname":"kd103663","forceEmailLogin":false,"localAvatarId":""}

was vom Server problemlos als application/x-www-form-urlencoded akzeptiert wurde.

Let’s build a PoC

Ein Proof of Concept soll bekanntlich die Sicherheitslücke demonstrieren. Um einen realistischen Angriff zu simulieren, habe ich folgende Aktionen per CSRF ausgeführt:

  • Änderung des Passworts auf “0” (fehlende Password policy)
  • Änderung der Mail-Adresse auf einen Random-String

Damit der Tester des PoC den Zugriff auf den Account nicht verliert, habe ich auf das anschließende Ausloggen der Session verzichtet.

Der PoC als Video

Eine Lücke kommt selten allein

Upload-Funktionen sind generell was tolles, vom Upload einer PHP-Datei, welche beim Aufruf ausgeführt wird bis hin zum Überschreiben von Systemdateien kann einem da alles passieren. Hier ging es dann eher in Richtung XSS durch Manipulation des Content-Types.

Fazit

GraphQL ist toll. Allerdings muss man, wie bei anderen Frameworks auch, an vielen Stellen noch nachbessern, um eine sichere Webseite betreiben zu können.

Nachdem ich die Lücken per Support-Ticket gemeldet hatte, erhielt ich recht schnell ein kleines Dankeschön. Leider haben es sich die Entwickler nicht gerade einfach beim Beheben der Lücken gemacht. So ist die CSRF-Problematik weiterhin noch nicht vollständig behoben. Lediglich ein eine unzureichende Prüfung des Orgin-Headers wurde anstelle eines CSRF-Tokens implementiert. Auf weitere Hinweise diesbezüglich reagierte das Unternehmen nicht.

Karikatur “Der Just-Origin Bieber” von @ProZion24

Bounty: 50€ Guthaben

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.