Hacklu CTF 2015 Writeups

Während den letzten beiden Tagen wurde das Hacklu CTF 2015 veranstaltet. Dabei handelt es sich um ein CTF (Capture the Flag) im jeopardy-Stil und Sebastian hat sein Glück einfach einmal versucht ;) In diesem Blogpost werden die folgenden Herausforderungen genauer beschrieben:

  • Module Loader (Web, 100)
  • PHP Golf (Coding, 75)
  • Guessthenumber (Coding, 150)
  • Bashful (Web, 200)

Zunächst einmal sei gesagt, dass CTF eine Menge Spaß bereiten können, besonders, wenn es einige knifflige Rätsel zu lösen gilt. Wenn man also noch nicht an einem CTF teilgenommen hat, dann sollte das dringend nachgeholt werden :). Aktuelle Infos zu CTFs finden sich unter: ctftime.org Während diesem Capture the Flag hat Sebastian mit Denis und Mazen160 zusammengearbeitet. Wenn man im Team arbeitet kann man sich gut ergänzen und einiges dazulernen.

Module Loader

Die erste Challenge war eine Aufgabe zum Aufwärmen: Eine Webapplikation nahm folgenden GET Parameter entgegen: $_GET['module'] und führte das darin übergebene Modul aus. Nach einem Blick in den Quellcode wusste man, wo sich die betreffenden Module befanden.

Bildschirmfoto vom Modul Loader im HTML Quellcode

Der Ordner /modules/ besaß eingeschaltetes Verzeichnis-Listing, weshalb es ohne weiteres möglich war, Zugriff auf weitere Module zu erhalten (deren Namen lassen sich einfach aus den Listing entnehmen).

Bildschirmfoto des eingeschalteten Verzeichnnis-Listing

Es war zudem möglich mittels Klick auf die einzelnen Module den Quelltext dieser zu betrachten - das erwies sich allerdings nicht als besonders nützlich. Durch Manipulation des Pfades sollte in einem nächsten Schritt ermittelt werden, ob die Webapplikation gegen eine sogenannte Local File Inclusion (LFI) anfällig war und dadurch Manipulationen möglich werden:

Bildschirmfoto der local file inclusion (LFI)

Das war schon einmal eine interessante Sache. Denis kam auf die Idee die .htaccess-Datei des Wurzelverzeichnisses einzubinden - gesagt, getan.

Bildschirmfoto der .htaccess-Datei des Modul Loaders

Der letzte Schritt war es die Datei flag.phpaus dem Verzeichnis einzubinden.

Bildschirmfoto der Flag des Modul Loaders

Bei einem CTF ist eine Challenge mit dem erlangen der Flag abgeschlossen - das Vorgehen war also erfolgreich und zum “Warmwerden” allemal geeignet. :)

PHP Golf

Die nächste Challenge war extrem interessant, denn es ging darum für folgende Konditionen ein Programm in PHP zu entwerfen:

Bildschirmfoto der PHP Golf Aufgabe

Zunächst wurde der Code ohne Berücksichtigung der Längenbeschränkung implementiert. Das Programm tat vermutlich das was es sollte, war allerdings zu lang - man musste zum Absolvieren der Challenge also reguläre Ausdrück nutzen.

Die nächste Version enthielt reguläre Ausdrücke und preg_replace mit einem e Modifier, um Groß- und Kleinschreibung entsprechend zu behandeln:

1
<?=preg_replace('/(\w)([^\w]*)(\w)?/e',"strtoupper('$1').'$2'.strtolower('$3')", $argv[1]);?>

Mit über 90 Zeichen war diese Version leider immernoch zu lang. In dem Moment war klar, dass die langen Funktionsaufrufe strtoupper / strtolower verkürzt werden müssen. Die Lösung sind sogenannte unicode character properties. Leider war es nicht möglich diese mit dem replacement Parameter der preg_match Funktion zu nutzen, sodass es erneut mit Perl versucht wurde:

1
<?=exec("echo '$argv[1]'|perl -pe 's~(\w)([^\w]*)(\w)?~\U\\1\E\\2\L\\3\E~g'");?>

Der Trick ist, dass alles zwischen \U und \E in Großbuchstaben umgewandelt wird. \L wandelt entsprechend in Kleinbuchstaben um. Leider war perl auf dem Submission-Server nicht verfügbar und der Payload war mit 80 Zeichen immer noch zu lang. Allerdings konnten wir mit exec und beliebigen Kommandos weiterarbeiten. Der nächste Gedanke fiel auf das Tool sed. Wie auch immer, es war bereits 2 Uhr morgens und wir entschieden uns nach Festhalten des Ansatzes auf der Mailingliste ins Bett zu gehen, um uns am nächsten Tag mit frischem Kopf der Aufgabe anzunehmen.

Am nächsten Morgen hatte Denis dann bereits eine funktionsfähige Lösung erarbeitet:

1
<?=exec("echo $argv[1]|sed -r 's/(\w)(\W*\w?)/\U\\1\L\\2/g'"); 

Einige Erklärungen zu dieser Lösung:

  • [^\w] ist dasselbe wie \W
  • <?= ist dasselbe wie <? echo
  • Man kann ?> weglassen, wenn der Code mit einem Semikolon abgeschlossen wird.

Diese Lösung hatte jedoch Probleme mit Unterstrichen _ im originalen String, aber nach einige Versuchen entstand ein Exemplar ohne diese und wir erhielten die Flagge :).

Sceenshot of php golf solution

Fertig

Guessthenumber

Die Aufgabe dieser Challenge war: Hundert Zahlen in der richtigen Reihenfolge zu raten.

Sceenshot of guessthenumber task

Die folgenden Hinweise wurden gegeben:

Es erschien am Einfachsten die Lösung mit Python umzusetzen, also googelten wir nach einer LCG Implementierung/Bibliothek und entdeckten dieses Beispiel. Der nächste Schritt bestand darin, die Paramter entsprechend den Vorgaben anzupassen und den grundlegenden Server/Client Kommunikationscode zu schreiben. Soweit ganz einfach.

Der Server teilte uns immer seine aktuelle Zeit mit. Diese hatte wahrscheinlich irgendwas mit dem Initialisierungsformat YmdHMS zu tun. Nach dem Extrahieren aller nötigen Informationen mit regulären Ausdrücken, konkatenierten wir diese folgendermaßen:

1
2
    timedata=str(year)+str(month)+str(day)+str(hour)+str(minute)+str(second)
    seed(int(timedata))

Die letzte wichtige Aufgabe bestand darin, den Modulo-Operator auf die erzeugten Zahlen anzuwenden. Sowohl 0 als auch 99 sind im gültigen Zahlenbereich, sodass wir rnd() %100 nutzen müssen.

Unglücklicherweise löste dieser Ansatz die Challenge nicht, da der erste Versuch immer falsch war. Es stellte sich heraus, dass man 100 Zahlen generieren und diese in umgekehrter Reihenfolge senden musste.

Sceenshot of guessthenumber flag

Der komplette “quick & dirty code”: Pastebin

Fertig :)

Bashful

An dieser Challenge haben wir die meiste Zeit verbracht, weil es viel Mühe gekostet hat. Mazen stieg bei dieser Challege mit ein. Okay, aber alles nacheinander. Bashful ist eine Webapplikation die in Bash geschrieben wurde, um Notizen speichern zu können.

Hier wird zunächst die finale Lösung gepostet, bevor andere Lösungsideen diskutieret werden. Dabei ist immernoch nicht sicher, ob diese Lösung beabsichtigt war, in jedem Fall funktioniert sie gut ;)

Screen of bashful flag extraction

Wie man sehen kann, war es möglich einen Shellshock) Payload in den Request Headern unterzubringen:

1
X-Foo: () { :;}; /bin/bash -c "cat /var/www/flag"

Psst: Es gab sogar eine XSS Lücke, in dem man den XSS Payload in den Headern versteckte :D

Die Lösung ist recht enttäuschend. Man hatte so viel Spaß mit dem Quelltext, und bei dessen Durchsicht fiel mir folgende drei Funktionen auf:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function explode {
        IFS="$1" read -ra "$2" <<< "$3"
}
function filter_nonalpha {
        echo $(echo $1 | sed 's/[^a-zA-Z0-9.!$;?_]//g')
}
function parse {
        explode '&' 'pairs' "$1"
        for pair in "${pairs[@]}"; do

            explode '=' 'keyval' "$pair"

            export $(filter_nonalpha "${keyval[0]}")="${keyval[1]}"

    done}

Diese Funktionen wurden später zum Parsen der Benutzereingaben verwendet:

1
2
3
if [ -v QUERY_STRING ]; then
    parse "$QUERY_STRING"
fi

Die erste interessante Sache ist das sed Kommando in der filter_nonalpha Funktion, da es alle Zeichen ersetzt, die nicht in den eckigen Klammern stehen. D.h. unsere Eingaben können .!$;? enthalten, was im Kontext der Bash sehr von nutzen sein kann. Die zweite interessante Sache ist die folgende Zeile aus parse:

1
export $(filter_nonalpha "${keyval[0]}")="${keyval[1]}"

Wir merken, dass nur die Variablennamen der Umgebungsvariablen gefiltert werden, jedoch nicht die Werte. Zusätzlich können wir parse nutzen, um beliebige Umgebungsvariablen zu setzen. Zum Beispiel mit dem Query String DEBUG=1 wird eine Variable $DEBUG mit dem Wert 1 belegt.

Etwas weiter unten im Quelltext fand sich folgender Block:

1
2
3
4
5
if [ -v DEBUG ]; then
    echo -ne '<pre>'
    printenv
    echo -ne '</pre>'
fi

Wie bereits gesagt, führt das Setzen von DEBUG= in der URL zur Ausgabe aller Umgebungsvariablen:

Screen of bashful debug information

Da wir nun wissen, dass wir andere Umgebungsvariablen setzen/überschreiben können, wird der folgende Codeblock sehr interessant:

1
2
3
4
5
6
7
8
9
10
11
sessid=$(filter_nonalpha $sessid)
if [ -z $sessid ] || [ "${#sessid}" -lt 60 ]; then 
   echo 'like... really?'
   exit
fi
sessfile=$SESSION_DIR/$sessid
if [ -f $sessfile ]; then
    explode '#' 'messages' "$(cat $sessfile)"
else
    messages=()
fi

Das Missbrauchen von $SESSION_DIR und $sessid zum Setzen eines beliebigen Pfades in der $sessfile Variable, um wiederum den Inhalt dieser Datei auszulesen, hörte sich nach einem tollen Weg an, um an die Flagge zu kommen. TL;DR: Das Setzen von $SESSION_DIR war nicht das Problem, sondern eher die Längenprüfung der $sessid Variable. So war es möglich die Beschränkung mit $IFS$IFS....$IFS zu umgehen, allerdings scheiterte dann die Datei-Existenzprüfung ([ -f $sessfile ]).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
if [ ! -v page ]; then
    page=home
else
    page=$(filter_nonalpha "$page")
fi
if [[ "$page" == "index" ]]; then
    page=home
fi
file="$DOCUMENT_ROOT/$page.sh"
if [ ! -f $file ]; then
    >&2 echo "Can't load $file"
    file="$DOCUMENT_ROOT/404.sh"
fi
source $file

Das sah sogar noch viel spannender aus, da es direkt zu einer RCE führen könnte. Wieder nutzen wir die bekannte Methode, um die Variablen $DOCUMENT_ROOT und $page zu bearbeiten. Die Kombination dieser beiden würde dann gesourced (ausgeführt) werden. Wir müssten nur unsere Kommandos in eine Datei schreiben, welche auf .sh endet und im Document Root oder woanders liegt.

Wir konnten uns zwei Wege überlegen, wie man die RCE ausnutzen könnte:

Der erste Weg bestand darin, $SESSION_DIR=/var/www/ und $sessid=aaaa...aaa.sh (60+ mal a und .sh) zu setzen. Dies würde zu $sessfile=/var/www/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.sh führen. Das Speichern einer Notiz mit diesen Parametern würde eine $sessfile Datei erstellen. Leider funktionierte das auf Grund fehlender Schreibrechte nicht. Wir erhielten nur einen 500er vom Server.

Die andere Idee bestand darin, $SESSION_DIR=/var/sessions und $sessid genauso wie oben zu setzen, um eine Session-Datei mit der Endung .sh zu erstellen. Der zweite Schritt würde darin bestehen, DOCUMENT_ROOT=/var/sessions und $page=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa zu setzen. Allerdings scheiterte auch dieser Versuch, da der Webserver die index.sh nicht finden konnte.

Im Nachhinein macht der letztere Ansatz nicht wirklich Sinn. Aber vielleicht gibt es ja irgendwelche “Bash-Magic” die genutzt werden könnte, um die Parameter entsprechend so zu setzen, dass die RCE ausgeführt wird. Wie auch immer: Es ist lustig zu sehen, dass die Lösung für diese Challenge einfach war, und wir viel zu übermotiviert herangegangen sind.

Andere Challenges

Wir hatte noch einen Blick auf andere Challenges, können jedoch nur ein paar Ideen bzw. Ansätze nennen. Möglicherweise sind alle nicht richtig und/oder etwas sinnlos:

Grading-Board (Web):

  • Irgendwas mit SQL Injection
  • Möglichweise muss man grant options nutzen, um anderen Zugriff auf die eigene Tabellen zu geben.

Dr.Bob (Forensic):

  • Das .vdi Image mit qemu-nbd einbinden
  • LVM volume, aber verschlüsselt und Passwort ist unbekannt.
  • VirtualBox nutzen, um den gespeicherten Zustand wiederherzustellen, aber keine Passwörter für die Nutzer bekannt.
  • Volatility testen/nutzen, um (nützliche) Informationen zu ermitteln
  • VM booten und init=/bin/bash rw Kernelparameter nutzen, um in eine root-shell zu gelangen. Nach verdächtigen Dateien suchen. Kein Glück :(

Teacher’s Pinboard (Web):

  • Am Ende der pickle.js steht, dass splice/slice verwechselt/vertauscht wurden und man es beheben soll.
  • Pickle ist irgendeine Enkodierung. Informationen aus dem Cookie accountinfo werden dekodiert und genutzt.
  • Idee: Die Single-Page-App speichern und pickle.js beheben. Hoffen, dass das hilft, die Flagge von den Default-Notizen zu extrahieren.

Zukünftige CTFs

Wir denken, dass wir öfters in zukünftigen Jeopardy-Stil CTFs mitmachen werden, wenn es unsere Freizeit erlaubt. Es macht wirklich wirklich viel Spaß und hilft einem neue Ideen/Wege zu erkunden.

Wir hoffen das hilft irgendwie weiter, Team der Internetwache.org