kd❯ g

fffff803'00000000 EB FE jmp short loc_HackinG

~ ./priv



CTF Ekoparty 2017, 2018 y 2019 (PWN!)

Este reto se trataba de desarrollar un exploit el cual debía obtener una ejecución de código a partir del binario. Este ejecutable fue de los CTF de Ekoparty 2017, de igual manera desarrollé los del 2018 y 2019 que lo pueden encontrar en el repositorio al final del post.

Ahora la idea de este post fue porque no existía un review de la solución y personalmente creo que es importante plasmar estos desarrollos en algún paper o lo que sea, ya me ayuda a aprender más mientras estoy explicando o enseñando de como lo desarrolle. Entonces para empezar con el análisis lo primero que hago es cagar el binario a IDA para ver su arquitectura y validar algunas cosas que pueden dar información rápida, este punto fue complicado, ya que no tenía los símbolos y con más de 500 funciones.



Así que el paso siguiente fue identificar cosas pequeñas de estas funciones y revertir para ver que hacían. Esto tomo un poco de tiempo pero pude identificar algunas relevantes como main y socket y luego pase a renombrarlas, así marco un camino en el análisis.



Como había dicho sobre un socket, este se listaba en el puerto 8888 esperando alguna interacción. Después de esto se debía cumplir algunas condiciones para ejecutar otro socket al puerto 4930.



Con este primer socket identifiqué algunos buffers en heap llamado a malloc() y otros bytes que seteaban tamaños y desplazamientos "offset" del buffer y otras variables. Aquí identifique una función de respuesta como send() la que utilice para filtrar memoria de 0x20 bytes, esto me ayudo a obtener direcciones stack, return, calcular punteros a vtable y sus funciones, chunk en heap, etc.



Después de realizar un leak de 0x19C0 bytes la conexión se cierra y crea una nueva en el puerto 4930 el que se genera multiplicando 8888 * 2 con la instrucción shl eax, 1 y espera un segundo envio de payload.



En esta segunda conexión analizamos la siguiente función que renombre como vulnerable. Luego de comprobar si el socket está correcto entramos a las validaciones.



Debo evitar algunas comparaciones de GetTickCount() que nos devuelve un valor random evitando que siga por el flujo, por suerte existe un envió de esta data, así que solo debo recibir estos bytes utilizarlos como bypass y además de calcular algunas constantes con este valor para saltar al las rutinas que me llevaran a comprobar la vulnerabilidad.



ya evitando el valor random debemos calcular este mismo valor con una constante para saltar a la función con el bug. Estos valores son los siguientes: (98543268h, 98543269h, 9854326Ch, 98543267h, 9854326Ah, 9854326Bh, 9854326Dh, 98543265h). La que resalta es la que nos interesa "hice un análisis previo". Por lo tanto el cálculo que debo hacer es asi 9854326D-vRandom=Result y al revertir la resta de Result+vRandom=9854326D con esto ya solucionamos el salto.



Solo nos queda revertir un poco más y ver como puedo explotar esta vulnerabilidad:

1 - En 9854326Dh encontré un free() luego de un malloc() y un strncpy(). Con estas funciones pude liberar una bloque luego volver a utilizar y copiar la data enviada en el primer payload a ese espacio como punteros.



2 - Realizo una la llamada a la función obtenida de la vtable. Aquí hay un pequeño gran problema, lo que pasa es que existe una indirección de rax+0x18, donde calcula el inicio de vtable hacia nuestra función que controlamos, el problema es que el primer puntero a la función no son bytes ejecutables entonces si saltamos ahí donde normalmente debería estar el inicio de nuestra shellcode este no ejecuta y se bloquea. Para solucionar este problema tuve que calcular el offset de donde quedo mi chunk en le heap y dejarlo en rax para así utilizar la indirección de rax+0x18 dentro de mi data y no a los punteros de las funciones.



3 - Con esto solucionado deje un desplazamiento de 0x18 dentro de mi data que es el primer puntero a mi ROP.



4 - La lógica del ROP que utilice hace un stack pivot a los punteros escritos en la función controlada, luego de procesar todos los gadget retorno al stack original y ejecuto system evitando una lectura fuera de límites.

Antes de pasar al detalle dejo este diagrama que realice, para poder explicar de manera gráfica como hice esta hazaña y como evite el primer puntero de la función de vtable.



* hago un stack pivot a los a ROP donde esta mi funcion falsa de vtable
xchg rsp, rcx / add rsp, 28h / retn

* guardo el puntero a calc en rcx
pop rcx / add [rax],al / add rsp, 38h / retn

* guardo el puntero a system desde función a rbx
pop rbx / retn

* muevo el puntero a system a rax para su copia
mov rax,rbx / add rsp,20h / pop rbx / retn

* aprovechando el pop rbx de la instrucción anterior guardo la dirección de stack original
pop rbx

* hago una copia del puntero a system en la dirección del stack original
mov [rbx],rax / add rsp,20h / pop rbx / retn

* por último paso la dirección del stack original a rsp donde se encuentra el puntero a system y salto a ahí
pop rsp / retn



Video:
https://www.youtube.com/watch?v=REy0G5abyas

Repo:
https://github.com/s1kr10s/CTF/2017
https://github.com/s1kr10s/CTF/2018
https://github.com/s1kr10s/CTF/2019

Code: