Skip to main content

Fetch the Flag CTF 2022: Roadrunner-Lösung

Artikel von

Giovanni Funchal

feature ctf roadrunner

9. November 2022

0 Min. Lesezeit

Danke, 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.

Roadrunner-Sandbox mit Go-Code, der „hello world“ ausgibt, einem Run-Button und einem Ausgabefeld mit „hello world“

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.

HTML-Codeausschnitt mit einem Formular, einem Label „Roadrunner Sand …“ und einer Schaltfläche „Ausführen“

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.

JavaScript-Code, das die Formularübermittlung verarbeitet und Skriptdaten mit einer POST-Anfrage an einen /run-Endpunkt sendet

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

Dockerfile-Code mit Argumenten für Debian- und Go-Versionen, Befehlen zur Paketinstallation und dem Kopieren von flag.txt in das Anwendungsverzeichnis

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.

Code-Snippet, der zeigt, wie ein Roadrunner-Server gestartet wird und auf einem lokalen Port lauscht

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

Code-Editor mit einer Go-Willkommensfunktion, die ein index.html-Template parst und ausführt.

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.

Dunkler Code-Editor mit einem Go-HTTP-Handler, der eine Anfrage decodiert, ein Skript bereinigt und ausführt und das Ergebnis schreibt.

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.

Go-Code für sanitizeScript, das Dateiänderungen, Systemaufrufe und Netzwerk-Imports blockiert.

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?

Codeausschnitt mit einem Go-Welcome-Handler, der ein index.html-Template parst und ausführt.

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.

Code-Dokumentation zur Go-Methode `Execute` für ein `Template` mit den Parametern Writer, Data und Error

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:

Screenshot mit dem Titel „Roadrunner Sandbox“: Go-Code, der eine flag.txt-Vorlage parst und die gerenderte Ausgabe ausgibt.

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.

Weiterlesen

illustration hero ai
Blog

PHP behebt zwei kritische unauthentifizierte RCE-Schwachstellen (CVE-2026-109618, CVE-2026-109619): Jetzt aktualisieren

Zwei kritische Sicherheitslücken im Request-Lebenszyklus von PHP können unauthentifizierte Remote Code Execution ermöglichen. Aktualisieren Sie auf eine korrigierte Version oder installieren Sie für ältere Versionen einen Backport des Anbieters.

blog feature ai lilac
Blog

Mit agentischer Behebung Ihren Schwachstellen-Backlog in Pull Requests verwandeln

Der Evo Remediation Agent ist jetzt in der öffentlichen Vorschau verfügbar und erweitert Evo Agentic AppSec um die automatisierte Auflösung von Abhängigkeiten. AppSec-Teams können jetzt kontinuierliche Behebungskampagnen durchführen

AI Background min crop
Blog

KI ruft alle Risiken auf den Plan, die Sie akzeptiert haben: Sieben Erkenntnisse aus dem AI Security Panel: Unfiltered

KI macht Angriffe günstiger und schneller und legt Risiken offen, die Unternehmen bereits tragen. Vier Sicherheitsverantwortliche erklären, warum Abwehr in Maschinengeschwindigkeit und schnellere Behebung jetzt unverzichtbar sind.