Fetch the Flag CTF 2022: Lösung zu „Pay Attention“
Assaf Ben Josef
9. November 2022
0 Min. LesezeitDanke, dass Sie mit uns Fetch gespielt haben! Herzlichen Glückwunsch an die Tausenden Spielerinnen und Spieler, die bei Fetch the Flag CTF dabei waren. Und ein riesiges Dankeschön an die Snyker, die die Challenges entwickelt, getestet und dokumentiert haben!
In diesem Beitrag sehen wir uns an, wie unser Team die Challenge „Pay Attention“ aus Snyks Fetch the Flag CTF 2022 angegangen ist. Die Challenge simuliert einen Fall, in dem ein beliebtes Paket vermutlich kompromittiert und mit Schadcode versehen wurde – und wir übernehmen die Rolle des Sicherheitsforschers, der den Vorfall untersucht!
Schritt-für-Schritt-Anleitung
Zu Beginn erhalten wir eine herunterladbare Datei pytest-7.1.3.tar.gz und den Hinweis „Haben Sie in letzter Zeit gut aufgepasst?“.
Sofort fällt uns der Dateiname auf. Pytest ist ein äußerst beliebtes Python-Testframework und zum Zeitpunkt dieses Beitrags ist Version 7.1.3 die neueste Version. Es scheint also, als sollte diese Datei ein Archiv der aktuellen pytest-Version sein.
Es kann sich natürlich nicht um eine exakte Kopie von pytest 7.1.3 handeln, sonst hätten wir keinen Anhaltspunkt! Um das zu überprüfen, rufen wir das pytest-GitHub-Repository auf und laden den Quellcode für Version 7.1.3 herunter. Wenn wir die Eigenschaften dieser Archivdateien untersuchen, stellen wir fest, dass die CTF-Version von pytest nur ein klein wenig größer ist als das Original.
Um herauszufinden, wo genau sich die beiden Archive unterscheiden, können wir die Ordner entpacken und mit dem Terminal-Befehl diff -rq vergleichen. Dieser durchsucht rekursiv die Ordnerinhalte nach Dateiänderungen. Das sieht dann so aus:

Wir entdecken einige kleinere Unterschiede in ein paar Metadatendateien. Doch noch wichtiger: Eine der eigentlichen Quellcodedateien, fixtures.py, unterscheidet sich zwischen den beiden Ordnern – das muss es sein!
Verdächtigen Code genau unter die Lupe nehmen
Wenn wir die Datei in unserem bevorzugten IDE-Programm untersuchen und ein wenig durchscrollen (oder die IDE uns auf eine unerwartete Einrückung hinweist), stoßen wir auf diese verdächtige Codezeile:
Auf den ersten Blick scheint diese Zeile einige Bibliotheken zu importieren und dann interessante Funktionsaufrufe wie b64decode und exec auszuführen. Das ist definitiv verdächtig, aber wir wissen noch nicht genau, was hier passiert. Also machen wir uns ans Entschlüsseln.
Wir beginnen mit den kürzeren Zeichenfolgen, die einem ähnlichen Muster folgen, zum Beispiel \x65\x78\x65\x63.
Vielleicht wissen einige von uns, dass Python 3 Unicode zur Darstellung von Zeichenfolgen verwendet. Wenn wir uns näher mit der Python-3-Dokumentation befassen, finden wir heraus, dass dieses Muster lediglich eine zweistellige hexadezimale Darstellung von Unicode-Codepoints ist.
Das müssen wir aber gar nicht wissen. Wenn wir die Codezeile betrachten, sehen wir, dass diese Zeichenfolgen unverändert an integrierte Python-Funktionen wie __import__() übergeben werden. Daher können wir davon ausgehen, dass der Python-Interpreter bereits weiß, wie er damit umgeht. Starten wir also eine Python-REPL, können wir die Funktion print() mit einer dieser Zeichenfolgen aufrufen. Sie gibt dann die lesbare Unicode-Darstellung aus. Zum Beispiel liefert print(‘\x65\x78\x65\x63’) die Zeichenfolge exec. Praktisch!
Nachdem wir all das aufgelöst haben, bleibt folgendes Ergebnis übrig:
Es sieht so aus, als würde dieses Skript eine Base64-Zeichenfolge dekodieren, kompilieren und anschließend ausführen. Als Nächstes möchten wir wissen, welcher Code hier ausgeführt wird. Dazu dekodieren wir die Base64-Zeichenfolge mit einem beliebigen Online-Dekodierungstool. Das erhalten wir:
Diesmal scheint der Code eine temporäre Datei zu erstellen, ein kurzes Skript hineinzuschreiben und es dann auszuführen. Das Skript in der temporären Datei lädt Code aus einer Online-Ressource (auf der Snyk-CTF-Domain) herunter und führt ihn aus.
Wir möchten den Hinweisen weiter folgen und schließlich herausfinden, welcher Code am Ende dieser Kette ausgeführt wird. Doch dann stoßen wir auf eine ziemlich harte Nuss: Der heruntergeladene Code ist vollständig verschleiert! Der einzige verständliche Teil steht ganz oben – dort erfahren wir den Namen des Tools, mit dem der Code verschleiert wurde, sowie dessen GitHub-Repository: Hyperion.
Unser erster Impuls könnte sein, dass wir mit dem verschleierten Skript und dem Quellcode des Verschleierungs-Tools einfach unsere Reverse-Engineering-Hüte aufsetzen und loslegen sollten. Da das Skript jedoch gut verschleiert zu sein scheint und in der README-Datei des GitHub-Repositorys des Tools steht, dass es derzeit keinen Deobfuscator gibt, beschließen wir, zuerst alle anderen Möglichkeiten auszuschöpfen.
Mit dieser Einstellung laden wir das verschleierte Skript zunächst in eine lokale Datei herunter und richten eine Debugging-Umgebung ein, um herauszufinden, ob sich zur Laufzeit etwas Interessantes tut. In unserer bevorzugten IDE können wir den Code Zeile für Zeile debuggen und ausführen, um zu beobachten, was passiert.
Sicherheitshinweis
In einem realen Szenario und bei einem Skript, das tatsächlich Schaden anrichten soll, würden wir all das in einer sicheren Sandbox-Umgebung durchführen!
Und tatsächlich entdeckt unser scharfes Auge schließlich die Flag zwischen den deklarierten Variablen, nachdem wir die Backslashes entfernt haben:

Gut aufgepasst!
Fassen wir also kurz zusammen:
Ein scheinbar harmloses Paket, in eine seiner Dateien wurde jedoch eine verdächtige Codezeile eingeschleust
Die Codezeile kompiliert ein Base64-kodiertes Skript und führt es aus
Dieses Skript erstellt eine temporäre Datei und führt sie aus
Diese Datei greift auf eine Remote-Ressource zu, lädt ein verschleiertes Skript herunter und führt es anschließend aus
Durch die Untersuchung und das Debuggen dieses verschleierten Skripts konnten wir schließlich die zwischen seinen Deklarationen versteckte Flag finden.
Diese mehrschichtige Verschleierung ist bei eingeschleustem Schadcode in Skripten sehr verbreitet. Was für ein Abenteuer!
Möchten Sie erfahren, wie wir alle anderen Flags gefunden haben? Auf unserer Seite mit den Fetch the Flag-Lösungen erfahren Sie, wie wir vorgegangen sind.



