Zeitgleich mit dem Dank die Challenge “Unknown zip archive” beendet zu haben, hat Jessica schon den nächsten Auftrag (Strange binary file) für uns. Diesmal geht es um eine merkwürdige Binärdatei, die uns als möglicherweise ausführbar, jedoch nicht als ELF-Datei, beschrieben wird.
Wir sehen uns als zuerst einmal den Inhalt der Datei etwas genauer an.
rup0rt@lambda:~/strange_binary_file$ xxd executable1.ndh 0000000: 2e4e 4448 8e04 1bdf 0301 0301 0103 0204 .NDH............ 0000010: 0201 0000 0402 0200 0004 0a01 0017 0101 ................ [...] 00003e0: f8fe 1c0e 0000 001a 0402 0015 8419 04cf ................ 00003f0: fe07 0108 2004 0002 0804 0101 0004 0103 .... ........... 0000400: 1f04 0100 0330 0400 0002 1904 e0fe 1904 .....0.......... 0000410: d2fe 1c02 0503 0708 0601 0953 6369 7465 ...........Scite 0000420: 656b 2070 726f 7465 6374 6564 2073 746f ek protected sto 0000430: 7261 6765 2023 310a 456e 7465 7220 796f rage #1.Enter yo 0000440: 7572 2070 6173 7377 6f72 643a 2000 7363 ur password: .sc 0000450: 6974 6565 6b2e 6e75 6974 6475 6861 636b iteek.nuitduhack 0000460: 2e63 6f6d 3a34 3030 3100 6d79 787a 4a61 .com:4001.myxzJa 0000470: 4b45 2e74 7874 0047 6f6f 6420 7061 7373 KE.txt.Good pass 0000480: 776f 7264 0a00 4261 6420 7061 7373 776f word..Bad passwo 0000490: 7264 0a00 rd..
Was besonders auffällt, ist der Anfang, der mit den Buchstaben “NDH” beginnt und die an dieser Stelle höchst wahrscheinlich der “Nuit du Hack” zuzuordnen sind. Wir erinnern uns, dass ELF-Dateien ähnlich beginnen, indem bei diesen auch der Dateityp -“ELF”- am Anfang der Datei zu finden ist.
Weiterhin auffällig ist das Ende der Datei, an dem die einzigen lesbaren Zeichenketten vorhanden sind. Die Position dieser Daten, wie auch deren inhaltliche Zusammenhangslosigkeit erinnern entfernt an Assembler-Quellcodes, in denen die Zeichenketten ebenfalls durch “DB”-Anweisungen an das Ende der Datei gestellt werden.
(Ich erspare an dieser Stelle meine wirren Versuche zu beschreiben, in den Binärdaten OP-Codes zu erkennen sowie diese auszuwerten 😉 und springe direkt an die Stelle, wo mir einiges klarer wurde.)
Bereits in der ersten Challenge des “Nuit du Hack 2012”, hatten wir einen Assembler-Quellcode zu entschlüsseln. Dieser weist nicht nur ebenfalls die Zeichenketten am Ende auf, sondern verwendet die Register “r0” bis “r5”, die nicht auf normalen Intel-Assembler schließen lassen, bei dem die Register “eax” oder “bx” heißen würden. Dies lässt vermuten, dass wir wohlmöglich hier bereits eine NDH-Datei im Quellcode vorliegen haben.
Wenn bereits die erste Challenge mit NDH-Dateien zu tun hatte, sehe ich mir auch die zweite Challenge “Unknown zip archive” nochmals etwas genauer an. Dabei hatten wir eine RAR-Datei zu entschlüsseln, aus der wir letztendlich ein weiteres Archiv gewinnen konnten, das ich mir zum Abschluss der Challenge nicht näher angesehen habe. Dies hole ich nun nach!
rup0rt@lambda:~/strange_binary_file$ tar xfz ../sciproc.tgz rup0rt@lambda:~/strange_binary_file$ ls sciproc rup0rt@lambda:~/strange_binary_file$ cd sciproc/ rup0rt@lambda:~/strange_binary_file/sciproc$ ls AUTHORS COPYING includes Makefile src_vm rup0rt@lambda:~/strange_binary_file/sciproc$ make gcc -c -W -Wall -ansi -Werror -pedantic -D _BSD_SOURCE -I./includes src_vm/main.c -o src_vm/main.o [...] gcc -c -W -Wall -ansi -Werror -pedantic -D _BSD_SOURCE -I./includes src_vm/syscall_pause.c -o src_vm/syscall_pause.o gcc ./src_vm/main.o ./src_vm/syntax.o ./src_vm/check_file_mode.o ./src_vm/check_aslr_mode.o ./src_vm/check_nx_mode.o ./src_vm/check_pie_mode.o ./src_vm/check_arg_mode.o ./src_vm/check_debug_mode.o ./src_vm/check_core_mode.o ./src_vm/xfunc.o ./src_vm/error_ndh_format.o ./src_vm/free_all.o ./src_vm/init_vmem.o ./src_vm/parse_binary.o ./src_vm/rand_aslr.o ./src_vm/rand_pie.o ./src_vm/save_binary.o ./src_vm/show_mem_debug.o ./src_vm/get_reg.o ./src_vm/execute.o ./src_vm/disass.o ./src_vm/segfault.o ./src_vm/console_functions.o ./src_vm/op_push.o ./src_vm/op_pop.o ./src_vm/op_nop.o ./src_vm/op_inc.o ./src_vm/op_dec.o ./src_vm/op_xor.o ./src_vm/op_or.o ./src_vm/op_and.o ./src_vm/op_not.o ./src_vm/op_add.o ./src_vm/op_sub.o ./src_vm/op_mul.o ./src_vm/op_div.o ./src_vm/op_call.o ./src_vm/op_ret.o ./src_vm/op_jmpl.o ./src_vm/op_jmps.o ./src_vm/op_test.o ./src_vm/op_cmp.o ./src_vm/op_jz.o ./src_vm/op_ja.o ./src_vm/op_jb.o ./src_vm/op_jnz.o ./src_vm/op_mov.o ./src_vm/op_end.o ./src_vm/op_xchg.o ./src_vm/syscall.o ./src_vm/syscall_exit.o ./src_vm/syscall_write.o ./src_vm/syscall_close.o ./src_vm/syscall_open.o ./src_vm/syscall_read.o ./src_vm/syscall_setuid.o ./src_vm/syscall_setgid.o ./src_vm/syscall_dup2.o ./src_vm/syscall_send.o ./src_vm/syscall_recv.o ./src_vm/syscall_socket.o ./src_vm/syscall_bind.o ./src_vm/syscall_listen.o ./src_vm/syscall_accept.o ./src_vm/syscall_chdir.o ./src_vm/syscall_chmod.o ./src_vm/syscall_lseek.o ./src_vm/syscall_getpid.o ./src_vm/syscall_getuid.o ./src_vm/syscall_pause.o -o vmndh rup0rt@lambda:~/strange_binary_file/sciproc$ ls AUTHORS COPYING includes Makefile src_vm vmndh rup0rt@lambda:~/strange_binary_file/sciproc$ ./vmndh Syntax: ./vmndh [OPTION] <binary> [FLAG] OPTION: -file Load binary -arg Binary argument (optional) FLAG: -aslr Enable ASLR -nx Enable NX bit -pie Enable PIE -debug Debug console -core Generates a core dump when segfault
Anscheinend haben wir soeben einen NDH-Player bzw. eine virtuelle Maschine gefunden, die es uns vielleicht ermöglichen könnte, die uns zugespielte Binärdatei genauer zu untersuchen. Dies probieren wir direkt aus:
rup0rt@lambda:~/strange_binary_file/sciproc$ ./vmndh -file executable1.ndh Sciteek protected storage #1 Enter your password: rup0rt Bad password
Und siehe da, es funktioniert! Auch die Zeichenketten kommen uns bereits von der Betrachtung der Rohdaten bekannt vor. Nun fehlt uns nur noch eine Möglichkeit das Passwort in Erfahrung zu bringen. Hierfür versuche ich direkt die Option “-debug”, die uns das “vmndh”-Werkzeug zu Beginn angeboten hat.
rup0rt@lambda:~/strange_binary_file/sciproc$ ./vmndh -file executable1.ndh -debug
[Console]#> help
<enter> Execute next instruction
run Run program
bp <addr> Set breakpoint
info bp Display info breakpoint
info reg Display registers
show sp Display SP memory
show pc Display PC memory
dis <addr>:<size> Disassembly X bytes from ADDR
x/x <addr>:<size> Print X bytes from ADDR
x/s <addr> Print string addr
set reg=value Set value in register
syscall Execute 'syscall' instruction
help Display this help
quit Quit console debugging
[Console]#> run
0x8000 > jmpl 0x3df
0x83e2 > movl r0, #0x8415
0x83e7 > call 0xfecf
0x82ba > mov r7, r0
[...]
Wir erhalten eine Console mit deren Hilfe wir das Programm nun zur Laufzeit untersuchen können. Die angebotenen Befehle sowie die Ausgabe des Testlaufes erinnern stark an den Debugger GDB. Mit diesem Wissen und den nun vorhandenen Möglichkeiten der Programmanalyse, machen wir uns an die Arbeit, das Passwort zu ermitteln.
[SYSCALL output]: Sciteek protected storage #1
Enter your password:
0x82d3 > ret
0x83eb > subb r8, #32
0x83ef > mov r2, r8
0x83f3 > movb r1, #0
0x83f7 > movb r3, #31
0x83fb > movb r0, #3
0x83ff > syscall (r0 = 0x0003 - read)
rup0rt
[SYSCALL output]: 7
0x8400 > mov r0, r2
0x8404 > call 0xfee0
[...]
0x8029 > pop r1
0x802b > ret
0x82f5 > cmpb r0, #9
0x82f9 > jz 0x05
0x82fc > call 0xffd4
[...]
0x82d2 > syscall (r0 = 0x0004 - write)
[SYSCALL output]: Bad password
Beim ersten Durchlauf des Binarys im Debug-Modus scheint das Programm zunächst die Länge des eingegebenen Passwortes, durch das Suchen nach dem Null-Byte, festzustellen und anschließend zu prüfen, ob dieses Neun Zeichen lang ist. Ein Breakpoint an der entsprechenden Adresse und die Ausgabe der Register soll das nun überprüfen:
[Console]#> bp 0x82f5
Breakpoint set in 0x82f5
[BreakPoint 1 – 0x82f5]
0x82f5 > cmpb r0, #9
[Console]#> info reg
[r0]: 0007 [r4]: 0000
[r1]: 0000 [r5]: 0000
[r2]: 7fda [r6]: 840d
[r3]: 001f [r7]: 7fda
[bp]: 7ffa [zf]: 0000
[sp]: 7fd8 [af]: 0000
[pc]: 82f9 [bf]: 0001
Das eingegebene Passwort “rup0rt” ist sechs Zeichen lang, das Programm zählt scheinbar das Null-Byte mit. Das heißt, dass ein Passwort der Länge acht diesen Vergleich bestehen sollte und uns hoffentlich weitere Programmschritte offenbaren wird. Zum Testen wähle ich nun das Passwort “12345678”.
0x82f5 > cmpb r0, #9 0x82f9 > jz 0x05 0x8301 > mov r0, [r7] 0x8305 > mov r1, [r6] 0x8309 > xor r0, r1 0x830d > cmpb r0, #120 0x8311 > jz 0x05
Und siehe da! Das Programm führt weitere Überprüfungen am Passwort durch, vermutlich um festzustellen, ob es korrekt ist. Eine Betrachtung der Register an dieser Stelle mit Hilfe eines Breakpoints schafft weitere Klarheit:
[Console]#> bp 0x8305
Breakpoint set in 0x8305
[Console]#> info reg
[r0]: 0031 [r4]: 0000
[r1]: 0002 [r5]: 0000
[r2]: 7fda [r6]: 840d
[r3]: 001f [r7]: 7fda
[bp]: 7ffa [zf]: 0001
[sp]: 7fd8 [af]: 0000
[pc]: 8309 [bf]: 0000
[Console]#> x/x 840d:8
0x840d: 02 05 03 07 08 06 01 09
[Console]#> x/x 7fda:8
0x7fda: 31 32 33 34 35 36 37 38
Das Programm lies also Zeichenweise aus den Adressen [r6] und [r7] (das ist unser eingegebenes Passwort), schreibt die Zeichen in die Register r0 und r1 und wendet eine XOR-Verknüpfung darauf an, das heißt, die Bits der einzelnen Buchstaben werden miteinander verglichen und liefern nur den Wert “wahr” (1), wenn die unterschiedlich sind. Anschließend wird durch das Programm überprüft, ob das Ergebnis dem Wert 120 (0x78) entspricht.
Da die XOR-Operation (^) umkehrbar ist, das heißt das doppelte Anwenden von XOR auf denselben Wert, eben wieder diesen ergibt, können wir das Passwort berechnen. Für das erste Zeichen X gilt:
X xor 0x02 = 120 | xor 0x02
X = 120 xor 0x02
Ein Perl-Einzeiler hilft uns bei der Berechnung:
rup0rt@lambda:~$ perl -e 'print 120^02'; 122
Wir müssten also das Zeichen 122 (das entspricht dem ASCII-Charakter ‘z’) eingeben, damit nach Anwendung von XOR 0x02 das Ergebnis 120 (0x78) korrekt überprüft werden könnte. Um nun nicht jedes Zeichen einzeln berechnen, eingeben und erneut mittels Breakpoints ermitteln zu müssen, sehen wir uns mit dem Kommando “dis” (disassemble) den Quellcode des Programmes direkt an.
[Console]#> dis 0x82f5:100
0x830d: cmpb r0, #78
0x8329: cmpb r0, #44
0x8345: cmpb r0, #73
0x8361: cmpb r0, #6b
0x837d: cmpb r0, #61
0x8399: cmpb r0, #3e
0x83b5: cmpb r0, #6e
0x83d1: cmpb r0, #5e
Zusammen mit dem Inhalt, den wir an Adresse 0x8301 ermittelt haben, lassen sich nun die Zeichen das Passwortes berechnen. Ich schreibe dazu ein kleines Perl-Skript, dass diese Arbeit für uns erledigt:
#!/usr/bin/perl -w # x/x 0x840d:8 @part1 = ( 0x02, 0x05, 0x03, 0x07, 0x08, 0x06, 0x01, 0x09 ); # dis 0x82f5:100 @part2 = ( 0x78, 0x44, 0x73, 0x6b, 0x61, 0x3e, 0x6e, 0x5e ); for ($i=0;$i<8;$i++) { print chr($part1[$i] ^ $part2[$i]); } print "\n";
Das Ausführen dieses Skriptes sollte uns nun das Passwort berechnen:
rup0rt@lambda:~/strange_binary_file$ ./executable1.pl zApli8oW
Ein Test, dieses Passwort nun an das NDH-Programm weiterzugeben, führt zu folgendem Ergebnis:
rup0rt@lambda:~/strange_binary_file/sciproc$ ./vmndh -file executable1.ndh Sciteek protected storage #1 Enter your password: zApli8oW
Das Programm liefert also nicht mehr die Ausgabe “Bad password.” sondern scheint mit der Eingabe zufrieden zu sein. Dass keine weiteren Daten ausgegeben werden liegt daran, dass versucht wird die Flagfile “myxzJaKE.txt” vom System auszugeben, die ich natürlich nicht besitzt. Das Passwort muss also in die Online-Version des NDH-Programmes eingegeben werden. Adresse und Port liefert uns die Datei freundlicherweise selbst, wie wir bereits bei der ersten manuellen Betrachtung der Binardaten erkannt haben.
Ein simples “netcat” an sciteek.nuitduhack.com auf Port 4001 mit Eingabe des Passwortes “zApli8oW” liefert nun die Lösung der Challenge die, blos noch an Jessica unter dem Betreff “strange binary file” gesendet werden muss.
Damit wurde diese Challenge erfolgreich abgeschlossen.