CSAW CTF Qualifiers 2012 –
Reversing 400

Bei dieser Challenge (Reversing 400) wird uns wiederholt nur “wortlos” eine Datei übergeben. Wir sind also wieder völlig auf uns gestellt, herauszufinden, wie und wo in diesem Binary der Schlüssel zur Aufgabe versteckt ist.

Wie gewohnt beginnen wir mit einer Untersuchung des Dateityps mit dem Tool “find”:

rup0rt@lambda:~/CSAW2012$ file csaw2012reversing
csaw2012reversing: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=0x012c3cf67d5aa15a9985ea064958921dc600c367, not stripped

Dieses Mal handelt es sich also um ein Linux-Binary, das für ein 64bit-System kompiliert wurde. Es wird daher Zeit, die entsprechende Virtuelle Machine zu starten (hier ein Debian GNU Linux 6.0) um die Datei näher zu betrachten.

Wie gewöhnlich lässt uns die Neugier nicht im Stich und wir führen die Datei zu aller Erst ohne Nachzudenken einfach aus! Als Antwort erhalten wir:

CSAW CTF 2012 - Reversing 400 - normal execution

Das Programm scheint uns hier den verschlüsselten Key zum Lösen der Challenge auszugeben, der uns jedoch in dieser Form überhaupt nichts nützt. Um zu prüfen, ob es sich wirklich um verschiedene Zeichen handelt – denn die Ausgabe wirkt durch die Fragezeichen recht merkwürdig – nutzen wir das Werkzeug “xxd”.

CSAW CTF 2012 - Reversing 400 - encrypted key

Es handelt sich also tatsächlich um einen 16 Byte langen, verschlüsselten String. Demnach muss nun ein Weg gefunden werden, diesen Key zu entschlüsseln!

Zunächst einmal bietet es sich an, sich den Assembler-Code, auf dem das Binary basiert, zu betrachten, in der Hoffnung, die Berechnung des Schlüssels oder vielleicht sogar den Schlüssel selbst vor der Verschlüsselung abgreifen zu können. Dazu verwenden wir das Tool “objdump”. Hier ein Auszug des disassemblierten Codes aus der Funktion “<main>”.

  40069b:    e8 29 ff ff ff           callq  4005c9 <encrypt>
  4006a0:    b8 fc 07 40 00           mov    $0x4007fc,%eax
  4006a5:    48 8d 55 e0              lea    -0x20(%rbp),%rdx
  4006a9:    48 89 d6                 mov    %rdx,%rsi
  4006ac:    48 89 c7                 mov    %rax,%rdi
  4006af:    b8 00 00 00 00           mov    $0x0,%eax
  4006b4:    e8 e7 fd ff ff           callq  4004a0 <printf@plt>
  4006b9:    bf ff ff ff ff           mov    $0xffffffff,%edi
  4006be:    e8 f1 fe ff ff           callq  4005b4 <done>
  4006c3:    48 8d 45 e0              lea    -0x20(%rbp),%rax
  4006c7:    48 89 c7                 mov    %rax,%rdi
  4006ca:    e8 24 ff ff ff           callq  4005f3 <decrypt>

Das Programm ruft hier die Funktion “<encrypt>” auf, bevor anschließend der String mittels “<printf>” bei Speicheradresse 0x4004b4 auf dem Bildschirm ausgegeben wird. Bis dahin kennen wir das Programm bereits, da das genau das Verhalten ist, das wir beim Ausführen schon beobachten konnten.

Danach wird bei Speicheradresse 0x4006be ohne weitere Bedingung, die Funktion “<done>” aufgerufen, wobei die Abarbeitung des Codes mit der Funktion “<decrypt>” bei Speicheradresse 0x4006ca nicht fortgesetzt wird. Der entscheidende Schritt, den Schlüssel zu erhalten, liegt also darin, das Programm dazu zu bewegen, die Funktion “<decrypt>” dennoch aufzurufen!

Da die Abarbeitung hier nicht an Bedingungen geknüpft ist, können wir von “Außen” wenig machen. Der einfachste und schnellste Weg erscheint mir daher, das Binary direkt zu verändern und den Aufruf der Funktion “<done>” aus dem Programmcode zu entfernen. Dies sollte dazu führen, dass kein Wegsprung mehr erfolgt und die Funktion “<decrypt>” bei der sequentiellen Abarbeitung aufgerufen wird.

Die Manipulation der Opcodes kann hier ganz einfach mit einem normalen Hex-Editor erfolgen – ich habe “bless” verwendet.

CSAW CTF 2012 - Reversing 400 - patching the binary

Die gezielte Suche nach den Opcodes von “call <done>” (E8 F1 FE FF FF), die aus dem Objekt-Dump abgelesen werden können, führt zu einem Treffer im Binary. Dies muss also die Stelle sein, an der der von uns ungewollte Abzweig im Programmcode erfolgt, der den Aufruf der Funktion “<decrypt>” verhindert.

Diese fünf Bytes müssen demnach verändert und im besten Fall aus dem Binary entfernt werden. Um jedoch nicht ungewollt Sprünge zu verfälschen, Programmbereiche zu verändern und so das gesamte Programm unbrauchbar zu machen, entfernen wir die Bytes nicht einfach, sondern ersetzen sie mit dem Opcode “NOP” (No Operation), der vom Prozessor einfach übergangen wird.

Der NOP-Opcode wird durch die hexadezimale Darstellung 0x90 repräsentiert, was bedeutet, dass die fünf Byte lange Call-Instruktion an Position 0x6be des Binarys durch fünf NOP-Instruktionen ersetzt wird. Das Ergebnis sieht folgendermaßen aus:

CSAW CTF 2012 - Reversing 400 - patched binary

Der Aufruf der “<done>”-Funktion sollte nun aus dem Programmcode getilgt sein und wir können das Binary mit unseren Änderungen abspeichern. Eine Erfolgskontrolle mit dem Tool “objdump” führt nun zu folgender Ausgabe:

  40069b:    e8 29 ff ff ff           callq  4005c9 <encrypt>
  4006a0:    b8 fc 07 40 00           mov    $0x4007fc,%eax
  4006a5:    48 8d 55 e0              lea    -0x20(%rbp),%rdx
  4006a9:    48 89 d6                 mov    %rdx,%rsi
  4006ac:    48 89 c7                 mov    %rax,%rdi
  4006af:    b8 00 00 00 00           mov    $0x0,%eax
  4006b4:    e8 e7 fd ff ff           callq  4004a0 <printf@plt>
  4006b9:    bf ff ff ff ff           mov    $0xffffffff,%edi
  4006be:    90                       nop
  4006bf:    90                       nop
  4006c0:    90                       nop
  4006c1:    90                       nop
  4006c2:    90                       nop
  4006c3:    48 8d 45 e0              lea    -0x20(%rbp),%rax
  4006c7:    48 89 c7                 mov    %rax,%rdi
  4006ca:    e8 24 ff ff ff           callq  4005f3 <decrypt>

Unsere Anpassungen werden vom Disassembler korrekt interpretiert, was vermuten lässt, dass dies auch der Prozessor bei Ausführung tun sollte. Wir starten also das veränderte Binary nochmals und bekommen diesmal folgende Ausgabe:

CSAW CTF 2012 - Reversing 400 - solution

Alles verlief erfolgreich und es war uns mit sehr einfachen Maßnahmen möglich, den Schlüssel zu erhalten.

Das Ergebnis lautet “csawissohard__:(“.

Leave a Reply

Your email address will not be published.