Fetch the Flag CTF 2022: Roadrunner-Lösung
Giovanni Funchal
9. November 2022
0 Min. LesezeitDanke, dass Sie mit uns Fetch gespielt haben! Glückwunsch an die Tausenden von Spielerinnen und Spielern, die bei Fetch the Flag CTF dabei waren. Und ein großes Dankeschön an die Snyker, die die Challenges entwickelt, getestet und dokumentiert haben!
Wenn Sie Escape Rooms mögen, wird Ihnen die Roadrunner-Challenge aus Snyks Wettbewerb Fetch the Flag 2022 gefallen! In diesem Blogbeitrag erkläre ich, wie ich an die Challenge herangegangen bin und sie gelöst habe: durch das Ausnutzen einer Eingabevalidierungsschwachstelle im Code, die uns ermöglicht, aus einer Sandbox auszubrechen.
Challenge
Können Sie einen Roadrunner überholen? Keine Chance, José!
Diese Challenge beginnt damit, dass sie uns verspottet. Werden wir die Flagge finden?
Zunächst erhalten wir Zugriff auf eine Webseite, auf der wir etwas finden, das einem Online-Code-Playground ähnelt. Solche Online-Playgrounds werden häufig zum Lernen und Experimentieren genutzt, ohne dass eine Sprach-Toolchain installiert werden muss. Für die Programmiersprache Go gibt es sogar einen offiziellen.
Wir können Go-Code eingeben und auf Ausführen klicken. Der Code wird an ein Backend gesendet, dort ausgeführt und das Ergebnis zurückgegeben. Anschließend zeigt die Webseite es an.
Außerdem erhalten wir den Go-Quellcode des Backend-Dienstes, auf dem die Webseite läuft (roadrunner.go), sowie die Dockerfile, mit der dieser Dienst paketiert wird.
Als Erstes sehen wir uns die Hinweise an, die wir erhalten haben, und erkunden die Umgebung.
Schritt-für-Schritt-Anleitung
Erkundung
Neugier ist beim Lösen solcher Challenges meist eine nützliche Eigenschaft. Deshalb habe ich mich zu Beginn erst einmal umgesehen und ein paar Dinge ausprobiert. Ich programmiere nicht in Go, also suchte ich online nach einem „Hello World“-Beispiel, fügte es auf der Webseite ein und klickte auf Ausführen. Als Ergebnis erhielt ich ein „Hello World“. Bis hierhin lief alles gut.

Dieses „Hello World“-Beispiel nutzt die Bibliothek fmt, um eine Nachricht in der Ausgabe auszugeben.
Dann untersuchte ich mit meinem Browser den Quellcode der Webseite. Dabei fiel mir auf, dass die Schaltfläche Ausführen mit einer submit-Aktion in einem Formular verknüpft war.

Anschließend sah ich mir den JavaScript-Handler an. Wie erwartet sendet er eine POST-Anfrage an den Backend-Endpunkt /run und legt dann den Inhalt des Ergebnisfelds anhand der Antwort fest.

Dann wandte ich mich dem Backend-Quellcode zu, der Teil der Challenge ist. Beginnen wir mit der Dockerfile:

Wir haben die Flagge gefunden! Das bereitgestellte Image enthält im Stammverzeichnis eine Datei namens flag.txt. Jetzt müssen wir sie nur noch abgreifen. Dazu brauchen wir eine Möglichkeit, den Inhalt dieser Datei auszulesen.
Eine Spur finden
Sehen wir uns roadrunner.go an. Die Funktion main ist immer ein guter Ausgangspunkt.

Hier sehen wir die beiden Endpunkte / und /run. Sehen wir uns zuerst / an. Dieser Endpunkt wird von der Funktion welcome verarbeitet.

Der Code verwendet hier die Go-Bibliothek html/template, um durch das Einlesen der Datei index.html und das Einsetzen von Attributen den HTML-Code der Webseite zu erzeugen. Interessant.
Sehen wir uns nun den Endpunkt /run an, der von der Funktion runner verarbeitet wird.

Hier passiert einiges Interessantes. Zuerst erstellt der Code eine Sandbox, übernimmt das Eingabeskript aus der Anfrage und schreibt es in eine Datei in der Sandbox. Danach wird das Skript bereinigt, um zu prüfen, ob es sicher ausgeführt werden kann. Dabei kann die Eingabe auch abgelehnt werden. Wird sie akzeptiert, führt der Code das Skript aus, erfasst das Ergebnis und gibt es in der Antwort zurück.
Als ich mir das ansah, fiel mir zunächst auf, dass auch das Feld dirname in der Struktur Sandbox aus dem Anfrage-JSON dekodiert wird und daher vom Benutzer kontrolliert werden kann. Dieses Feld wird anschließend zum Erstellen eines Dateipfads verwendet – eine Schwachstelle. Ich war mir jedoch nicht sicher, wie ich sie ausnutzen könnte, und beschloss daher, mich wieder auf den Bereinigungsschritt zu konzentrieren. Sehen wir uns sanitizeScript genauer an.

Dieser Code analysiert das Eingabeskript und prüft anschließend die Imports. Eine Sperrliste enthält die gängigsten Go-Bibliotheken zur Dateiverarbeitung. Importiert unser Skript eine dieser Bibliotheken, wird es mit einer witzigen Meldung abgelehnt.
Denken Sie daran: Unser Ziel ist es, den Inhalt der Datei /flag.txt auszulesen.
Zuerst dachte ich, die Liste wäre möglicherweise nicht vollständig oder es gäbe andere Möglichkeiten, Dateien auszulesen. Wie bereits erwähnt, programmiere ich nicht in Go und suchte daher ein paar Minuten online.
Die Erkenntnis
Erinnern Sie sich an eine andere Stelle, an der wir zuvor Dateien eingelesen haben?

Genau, wieder die Funktion welcome. Sie liest eindeutig eine Datei ein und verwendet dafür die Template-Bibliothek, die nicht auf der Sperrliste von sanitizeScript steht. Vielleicht können wir sie nutzen?
Also sah ich mir die Dokumentation dieser Bibliothek an. Nachdem ein Template-Objekt erstellt wurde, führen wir das Template aus, das das Ergebnis in den Writer schreibt.

Wir müssen die Schnittstelle io.Writer in einen String umwandeln, den wir dann ausgeben können. Nach weiterer Recherche fand ich heraus, dass sich dafür strings.Builder eignet (die Bibliothek strings steht ebenfalls nicht auf der Sperrliste).
Unsere endgültige Lösung sieht also so aus:

Mit diesem Code-Snippet wird der gesamte Inhalt der Datei flag.txt in die Ausgabe geschrieben. Geschafft!
Roadrunner: Fazit
Was haben wir also gelernt? Wir haben sorgfältig konstruierte, nicht vertrauenswürdige Eingaben verwendet, um eine Schwachstelle in der Eingabevalidierung eines Online-Code-Playgrounds auszunutzen. Aufgrund eines Fehlers bei der Skriptbereinigung konnten wir aus der Sandbox ausbrechen. Kenntnisse über die internen Abläufe der Anwendung waren hilfreich: So konnten wir unsere Idee schnell in einen Exploit umsetzen. Das war nur eine CTF-Challenge. In der realen Welt erfordert die Entwicklung eines solchen Exploits möglicherweise etwas Ausprobieren und Raten.
Snyk Code – ein kostenloses Tool für statische Anwendungssicherheitstests (SAST) – hilft Ihnen, Schwachstellen in der Eingabevalidierung Ihres Codes zu finden und zu beheben.
Auch andere Techniken wie mehrschichtige Sicherheit können die Ausnutzung dieses Exploits erschweren. Beispielsweise könnte die Funktion runScript so geändert werden, dass das Skript mit einem anderen Benutzerkonto und eingeschränkten Berechtigungen ausgeführt wird. In diesem Fall wäre die Schwachstelle allein nicht ausnutzbar, es sei denn, sie würde mit einer zweiten Schwachstelle kombiniert – beispielsweise einer Rechteausweitung.
Die sichere Ausführung nicht vertrauenswürdigen Codes in einer Sandbox ist bekanntermaßen schwierig. Sandboxes sollen verhindern, dass Anwendungen auf andere Ressourcen und Daten zugreifen. In der Praxis sollten Sie nicht vertrauenswürdigen Code möglichst gar nicht ausführen.
Das war’s, Leute. Ich hoffe, es hat Ihnen gefallen! Möchten Sie erfahren, wie wir alle anderen Flaggen gefunden haben? Auf unserer Seite Fetch the Flag: Lösungen erfahren Sie, wie wir vorgegangen sind.
Starten Sie mit Capture the Flag
Erfahren Sie in unserem virtuellen On-Demand-Workshop für Einsteiger, wie Sie Capture-the-Flag-Herausforderungen lösen.



