Kaum haben wir die vorherige Challenge gelöst, meldet sich Jessica erneut mit einem Auftrag (Strange binary file #2). Sie sendet unter anderem eine weitere Binärdatei, über die sie Informationen verlangt. Da wir aus der Challenge “Strange binary file” nun bereits wissen, wie mit diesen .NDH-Dateien umzugehen ist, machen wir uns direkt ans Werk.
Bei der ersten Debug-Ausführung mit Eingabe eines beliebigen Passwortes fällt bereits auf, dass mehrfach folgende Überprüfung statt findet.
rup0rt@lambda:~/strange_binary_file_2$ ./vmndh -file executable2.ndh -debug [Console]#> run [...] 0x8538 > cmpb r0, #11 0x853c > jnz 0x03 0x8542 > cmpb r0, #1 0x8546 > jnz 0x07 0x8550 > cmpb r0, #2 0x8554 > jnz 0x07 0x855e > cmpb r0, #3 0x8562 > jnz 0x07 0x856c > cmpb r0, #4 0x8570 > jnz 0x07 0x857a > cmpb r0, #5 0x857e > jnz 0x07 0x8588 > cmpb r0, #6 0x858c > jnz 0x07 0x8596 > cmpb r0, #7 0x859a > jnz 0x07 0x85a4 > cmpb r0, #8 0x85a8 > jnz 0x07 0x85b2 > cmpb r0, #9 0x85b6 > jnz 0x07 [...]
Folgt man den einzelnen Sprungmarken, stellt man fest, dass hinter jeder Zahl verschiedene Anweisungen stecken, die das Kopieren von Daten, das Verschieben, Shiften oder anderweitige Manipulationen durchführen.
[Console]#> bp 0x8538
Breakpoint set in 0x8538
[Console]#> run
[…]
0x8337 > movl r0, #0x9
0x833c > mov r1, [r0]
0x8340 > mov r2, [r1]
0x8344 > inc r1
0x8346 > mov [r0], r1
0x834a > mov r0, r2
0x834e > pop r2
0x8350 > pop r1
0x8352 > ret
[BreakPoint 1 – 0x8538]
0x8538 > cmpb r0, #11
[Console]#> x/x 0x9:10
0x0009: 0b 0a 06 06 00 00 0b 02 07 4d 06 00 07 02 07 78
Ein Breakpoint während dieser Überprüfung zeigt, dass die Zahlen um Manipulationen durchzuführen, von der Adresse 0x09 geladen werden und vermutlich fortlaufend ausgewertet werden. Es scheint sich also um Opcode innerhalb des Opcodes zu handeln, der entweder das Passwort auf Korrektheit überprüft oder aber sich selbst – polymorph – erzeugt.
Sieht man sich die Operationen der einzelnen Opcodes per Breakpoints nun etwas genauer an, wird man beim Opcode 0x07 folgenden Quellcode finden:
[Console]#> dis 0x8588:10
0x8588: cmpb r0, #06
0x858c: jnz 0x0007
0x858f: call 0xfe11
0x8593: jmpl 0xff9e
0x8596: cmpb r0, #07
[Console]#> bp 0x858f
Breakpoint set in 0x858f
[Console]#> run
[BreakPoint 1 – 0x858f]
0x858f > call 0xfe11
[…]
0x83cc > xor r0, r2
0x83d0 > mov r1, r0
0x83d4 > mov r0, r3
0x83d8 > call 0xff42
Nachdem offensichtlich eine entsprechende Anzahl von Parametern eingelesen wurde, erkennten wir eine für uns aus der Challenge “Strange binary file” bekannte Operation: XOR. Wir setzen also einen Breakpoint vor die XOR-Operationen und versuchen genauer zu Untersuchen, was diese Anweisung an der Stelle bewirken soll.
[BreakPoint 1 - 0x831d] 0x831d > ret [Console]#> info reg [r0]: 0072 [r4]: 0000 [r1]: 0035 [r5]: 0000 [r2]: 004d [r6]: 0000 [r3]: 0000 [r7]: 0000 [bp]: 7ffa [zf]: 0001 [sp]: 7fec [af]: 0000 [pc]: 83cc [bf]: 0000 [Console]#> 0x83cc > xor r0, r2
XOR soll also auf die Zahl 0x72 (114) und 0x4d (77) angewendet werden, was 0x3f ergibt. 0x72 entspricht hierbei dem Buchstaben “r”, der dem Anfang des von mir gewählten Passwortes “rup0rt” entspricht. Wir können an dieser Stelle also bereits fast sicher sein, dass hier eine Art Überprüfung des Passwortes statt findet.
Lässt man das Programm weiter laufen und sieht sich die Register beim selben Breakpoint nochmals an, ergibt sich folgendes Bild:
[BreakPoint 1 - 0x831d] 0x831d > ret [Console]#> info reg [r0]: 0078 [r4]: 0000 [r1]: 003f [r5]: 0000 [r2]: 0000 [r6]: 0000 [r3]: 0008 [r7]: 0000 [bp]: 7ffa [zf]: 0000 [sp]: 7fee [af]: 0000 [pc]: 84b2 [bf]: 0000 [Console]#> 0x84b2 > mov r2, r0 [Console]#> run 0x84b6 > cmp r1, r2
Nach dem XOR wird also verglichen, ob das Ergebnis gleich dem Wert 0x78 (120) ist. Dies ist bei Eingabe des Buchstabens “r” in diesem Fall nicht so (0x3f). Wie also berechnen wir das erste Zeichen des Passwortes nun so, dass es nach dem XOR mit 0x4d (siehe Oben) wieder 0x78 ergibt?
Dazu wieder eine kurze Rechnung, wobei x dem von uns gesuchten Zeichen entspricht:
x XOR 0x4d = 0x78 | XOR 0x4d (da wir wissen, dass ein doppeltes XOR wieder den
Ausgangswert ergibt)
x = 0x78 XOR 0x4d
Mit Hilfe eines Perl-Einzeilers erhalten wir:
rup0rt@lambda:~$ perl -e 'print 0x78^0x4d'; 53
Die Dezimalzahl 53 entspricht hierbei dem Zeichen “5”. Dies sollte also den Anfang des von uns gesuchten Passwortest darstellen. Anders als in der Challenge “Strange binary file” lassen sich hier die XOR-Operanden nicht einfach aus dem Speicher auslesen, da sie erst durch den Opcode-im-Opcode gebildet werden.
Um nun nicht auch noch die anderen Opcodes untersuchen zu müssen und wohlmöglich wichtige Zeit zu verlieren, wähle ich nun ein Passwort, das mit “5” beginnt, setze wiederum einen Breakpoint vor die XOR-Operation auf 0x831d und lese die Operanden Zeichen für Zeichen aus, wie ich es bereits für den ersten Buchstaben getan habe.
Dies gibt nach einigen Minuten folgendes Ergebnis:
– 1. Zeichen: 0x4d XOR 0x78 = 53 (“5”)
– 2. Zeichen: 0x61 XOR 0x02 = 99 (“c”)
– 3. Zeichen: 0x72 XOR 0x43 = 49 (“1”)
– 4. Zeichen: 0x31 XOR 0x45 = 116 (“t”)
– 5. Zeichen: 0x30 XOR 0x03 = 51 (“3”)
– 6. Zeichen: 0x4c XOR 0x7f = 51 (“3”)
– 7. Zeichen: 0x64 XOR 0x0f = 117 (“k”)
Das Passwort lautet somit “5c1t33k“.
Ein kurzer Testlauf mit dem Debugger liefert keinen Fehler mehr, sondern:
0x81d2 > movb r0, #2
0x81d6 > syscall (r0 = 0x0002 - open)
[SYSCALL output]: 65535
0x81d7 > ret
0x8219 > cmpl r0, #0xffff
Es wird also versucht die Flagfile zu öffnen, die jedoch nur online vorhanden ist. Die Eingabe in die Online-Version des NDH-Programmes liefert anschließend den Text, der nun nur noch an Jessica mit dem Betreff “Strange binary file #2” zurück gesendet werden muss, um die Challenge erfolgreich abzuschließen.