Write Up 5h311 – Final CTF NCN 2014
Buenas,
Para continuar con la saga de write-up’s iniciada en el anterior post con @julianvilas, hoy os pondremos la solución a otro de los retos que tuvimos el placer de testear.
Se trata de un fichero llamado “5h311”, que al ejecutarlo se abre un servicio en local en el Puerto 6969, que la conectarnos muestra lo siguiente:
Parece ser un acceso por consola que permite una serie de comandos, concretamente “cat”, “echo”, “env”, “exit”, “help”, “ls”, “set” y “unset”.
Si hacemos ls encontramos que el flag se encuentra en el mismo directorio del binario, pero al hacer un “cat flag.txt” podemos observar que no disponemos de privilegios para accede .
Bueno pues nos ponemos manos a la obra y abrimos el programa con IDA para ver que es lo que nos esconde.
Buscamos en IDA las ocurrencias del comando “cat” con la búsqueda de Strings (ALT+T), con la finalidad de identificar la sección de código donde se están gestionando los comandos:
Como resultado, en la sección .rodata podemos observar el vector que incluye los comandos que son printados por pantalla.
Pero observando unas direcciones de memoria más abajo, se pueden observar de nuevo los comandos asignados a unos offsets, pero con la peculiaridad que aparece un nuevo comando que no nos ha listado el comando HELP, el comando “Puts”.
Miramos las Xrefs del offset correspondiente a “aPuts” (x Key):
Miramos de analizar la primera ocurrencia, que parece ser que se está empleando dentro de una función.
Antes de nada un apunte, observando el programa desde la View Graph, ya se observa algo raro, como si se hubiera ofuscado con algún tipo de programa, debido a que todos los paths van a parar a un mismo punto como se observa en la siguiente imagen:
Procedemos a analizar el bloque de código que emplea el offset de «puts», y observamos que se realizan una serie de comparaciones:
Concretamente hace una comparación de dos offsets con una llamada a strcmp y verifica mediante el siguiente cmp si son iguales, en ese caso activa el ZF.
Antes de continuar refrequemos que hace el opcode “setz”:
- The setz sets the byte in the destination operand to 1 if the Zero Flag (ZF) is set, otherwise sets the operand to 0.
Después mediante la instrucción “setz”, se setea el valor de “bl” a 1 si ZF vale 1, es decir bl valdrá 1 si el resultado del strcmp es que son iguales.
Posteriormente mediante la instrucción “test”, se modifica otra vez el ZF en función del resultado del registro bl. (Empezamos a observar algo de redundancia aquí, por lo que la hipótesis de que se ha empleado algún tipo de programa para ofuscar el código es cada vez más firme :P)
Finalmente se mueven de nuevo los dos valores comparados en el strcmp, y se ejecuta la instrucción cmovz (al parecer sin valor alguno debido a que solo se ejecuta si ZF=1, es decir, si ecx y eax ya son iguales).
Como podéis ver, una locura para al final solo acabar realizando una comparación xD Por lo que antes de ingresar en un psiquiárico, procederemos a realizar un análisis dinámico partiendo del bloque de código identificado.
Cuando el proceso recibe una conexión al puerto 6969, éste crea un nuevo proceso hijo mediante la syscall ‘clone’ y le pasa el control al mismo para que continúe su ejecución. Para poder debugear el proceso hijo sin que el padre nos moleste (al cabo de un tiempo envia un SIGALARM y nos mata el proceso), empleamos la instrucción “catch” de GDB para que, una vez cree el proceso hijo, podamos pausar su ejecución, y de esta forma poder debugear el proceso hijo con calma.
Añadimos un breakpoint a la dirección del bloque de código donde hemos observado el offset del ‘puts’, y ejecutamos la instrucción para ver que ocurre. Observamos que no se observa ningún cambio, por lo que el flujo de ejecución no pasa por ahí. (ouch!) Continuemos con el análisis pues.
Observando más detenidamente el código y continuando con la búsqueda de cadenas de texto interesantes como los comandos encontramos una zona de memoria donde se definen los offsets de las funciones asociadas a cada comando de la shell analizada:
En la imagen se muestran los offsets de las funciones ya renombrados.
Usando la funcionalidad de cross-reference vemos que el primero de estos offsets es llamado desde otro punto del código:
En ese punto se aprecia el acceso a este offset con un desplazamiento dinàmico con el valor almacenado en [eax*8]. Esto permite con el valor de eax recorrer la zona de memoria que contiene los punteros a las funciones de cada comando. Más tarde este offset guardado en el registro eax será llamado con call eax.
Tirando del hilo veremos que el valor de eax viene condicionado supuestamente por el valor del comando introducido por el usuario, mediante el offset «off_804E848».
Vemos que este valor parece venir de la función _fgets. Aunque de momento, debido a que el flujo del código esta ofuscado con la implementación de una máquina de estados, no podemos estar seguros de todas estas conjeturas. Deberemos por lo tanto ejecutar el análisis de forma dinámica para estar seguros.
Para analizar todos los valores comentados anteriormente de forma dinámica, utilizaremos gdbserver para debuggar cómodamente de forma remota y poder indicar los breakpoints directamente desde IDA.
Luego en IDA seleccionamos el Debugger de tipo GDB server y configuramos la ip y el puerto:
Ya podemos a priori centrarnos en el comportamiento de los comandos que más nos interesen, en este caso ‘cat’, ‘set’, y el misterioso ‘puts’. Para ello prepararemos el entorno para debuggar y pondremos breakpoints en los puntos más interesantes.
Focalizando en las funciones cat, set, i puts vemos que todas están implementadas ofuscando el flujo del código con una máquina de estados. Por lo tanto buscaremos las llamadas mas interesantes para poner breakpoints. Para verificar que no se nos escapa nada y por donde podemos empezar a mirar, sacamos un grafo de llamadas desde cada función que nos interesa con Graphview.
Ponemos los breakpoints en las llamadas empleadas en cada una de las funciones anteriormente mostradas en los callgraphs (strcmp, fopen, fgets, strncpy), con la finalidad de identificar, por ejemplo, las comparaciones realizadas en cada una de las llamadas a “strcmp”, y empezamos a jugar.
En la función «cat» observamos se emplea una llamada a fopen para abrir el fichero a leer y que el valor de dicho fichero esta hardcodeado a “flag.txt”, por lo que solo se podrá abrir un fichero con dicho nombre.
En la función «set» observamos que las variables de entorno se guardan en memoria empezando en el offset dword_804e8a8 sumando un desplazamiento de 0x100 bytes (por lo que resulta en 804e9a8) y que el numero de variables de entorno se guarda en el offset dword_804e8a4.
Podemos ver como en las direcciones de memoria anteriormente comentadas se almacenan las variables de entorno seteadas con el comando «SET», (variable en 0x804e8a8 y valor en un offset de 0x100, concretamente en 0x804e9a8).
En la función “puts” observamos que recorre las variables definidas con set (incluso recorre las que se han “eliminado” con unset pero que aún siguen en la memoria pero sin la primera letra) y compara su valor con la cadena “puts”.
Si una de las variables se llama “puts” entonces compara su valor con “printf”.
Probamos el comportamiento creando una variable de entorno que se llame puts y que su valor sea printf y luego invocamos a puts. Como podemos ver, parece que se hemos conseguido una escalada de privilegios o algo parecido.
Probando ahora el comando cat con un fichero cualquiera del directorio desde donde ejecutamos la shell vemos que nos devuelve “error: permission denied”
Pero si recordamos que el valor de «flag.txt» está hardcodeado en el binario, miremos de probar pues de modificar el fichero de nuestro entorno local a dicho nombre. Volvemos a provar y ya podemos mostrar el contenido de flag.txt!
En este caso esta prueba no logramos resolverla dentro del tiempo del CTF. Pudimos resolverlo con calma luego desde casa 😛
Solo queda dar gracias al staff de las NCN por la originalidad de los juegos!
Post escrito con la colaboración de «Pau Rodriguez», miembro activo de 0xb33r$ 🙂
Valuable information. Lucky me I found your web site accidentally, and
I am surprised why this coincidence didn’t took
place earlier! I bookmarked it.
21 abril, 2017 en 19:45