Narnia - [OverTheWire]

Cover Image for Narnia - [OverTheWire]
Marmeus
Marmeus

Table of Contents

    Información

    El wragame de Narnia esta compuesto de diferentes retos para que el jugador se inicie en el mundo de la explotación binaria. Se recomienda que antes de introducirse con los retos de Narnia se hayan realizado los retos del wargame Leviathan.

    Para acceder a los retos, es igual que en los anteriores wargames.

    ssh -p 2226 narniaX@narnia.labs.overthewire.org

    Donde X simboliza el nivel (0, 1, 2, 3 …) y la contraseña para el primer nivel es narnia0.

    Por un lado, tanto el código fuente como los ejecutables los puedes encontrar en /narnia/. Además, estos binarios son solamente ejecutables por el usuario del nivel anterior y el usuario del nivel que los creo.

    Por otro lado, las contraseñas para acceder al siguiente nivel se encuentran en /etc/narnia_passs.

    Finalmente, como en todos los retos de Over The Wire, se aconseja que te crees tu propia carpeta temporal en “/tmp/” para poder crear y ejecutar tus exploits.

    Narnia 0

    En este nivel tenemos que sobrescribir la variable “val” por el valor *0xdeadbeef *con la finalidad de que nos genere una shell como el usuario “narnia1”.

    Como se ver en la línea 25, scanf solamente recogerá los primeros 24 caracteres. Sin embargo, el buffer tiene un tamaño de 20 caracteres, por lo que los 4 siguientes valores serán los que sobrescriban la variables “val”.

    Además, por si eres nuevo en el mundo del exploiting de binarios, un sistema operativo, entre muchas cosas, tiene 2 modos de almacenar los datos. En little endian, si un usuario escribe “ABCD” el ordenador lo almacenará en memoria como “DCBA”. Y con big endian, de la misma forma que se ha escrito. En este caso estamos tratando con un sistema en little endian.

    python -c "import sys; print(sys.byteorder)"

    Una vez dicho esto, solamente tenemos que escribir las 20 As y la palabra en hexadecimal 0xdeadbeef, obteniendo el siguiente exploit.

    Es necesario ejecutarlo de la forma que se indica en el comentario porque al usar un pipe el script enviará los datos a narnia0 y se terminará de ejecutar ambos programas. Por lo tanto, para evitar que se cierren y poder seguir enviando información al programa vulnerable, es necesario usar “cat -”. El cual recibe los caracteres mediante el input estándar y los envía a través de la pipe al programa vulnerable.

    Finalmente, ejecutando el script obtenemos la shell necesaria para obtener la contraseña del nivel 1.

    Narnia 1

    Este nivel es algo que nunca se va a ver en la vida real, pero esta preparado para que se aprenda que es una variable de entorno y que son los punteros a funciones punteros.

    Por un lado, en Linux, las variables de entorno son marcadores de posición para la información almacenada dentro del sistema que pasa datos a los programas iniciados en shells (intérpretes de comando) o sub-shells. Estas variables se pueden listar mediante el comando:

    printenv

    Para declarar una variable de entorno se usa el comando export.

    export EGG=PATATA

    Mientras que para eliminar una variable se utiliza el comando unset.

    unset EGG

    Por otro lado, los punteros a funciones son variables que almacenan la dirección de una función. En nuestro código, la dirección será la que le proporcione la instrucción “getenv”. Por lo tanto, todo lo que declaremos en la variable EGG será ejecutado por el programa al llamar a “ret” en la línea 28. Las instrucciones almacenadas en la variable EGG deben de encontrase en ensamblador, porque el binario solo es capaz de entender instrucciones en binario. Para superar este nivel se necesita obtener una consola, por lo que hay que proporcionar una serie de instrucciones al programa de manera que nos genere una shell. A esta serie de instrucciones se conocen como shellcode, en este enlace se pueden encontrar una gran cantidad de shellcodes diseñados para diferentes sistemas operativos y arquitecturas.

    Para este programa se ha empleado el shellcode que se puede ver en la siguiente imagen y que se puede conseguir a través del siguiente enlace.

    Una vez tenemos el exploit, falta ejecutarlo para que se almacene en la variable EGG y poder obtener nuestra shell.

    Narnia 2

    Este nivel se basa en sobrescribirde la dirección de retorno de la función main, con la finalidad de redirigir el tráfico del tráfico del programa a otra función, obtener una shell y obtener la contraseña para el siguiente nivel.

    Si eres nuevo en el mundo de la explotación binaria, las funciones de un programa a los ojos del ordenador tiene la siguiente forma.

    Donde EBP (Extended Base Stack Pointer) y **ESP **(Extended Current Stack Pointer) son punteros que apuntan al principio y al final de de una función (No se tienen en cuenta los parámetros ni la dirección de retorno).

    En este caso, no hay variables escalares, solamente una única variable vectorial, la cual podemos desbordar, y sobrescribir la dirección de retorno. Para averiguar cuantos caracteres son necesarios para sobrescribir la dirección de retorno, se ha escrito el siguiente programa.

    Además, se ha empleado la herramienta GDB para depurarlo y obtener los caracteres que sobrescriben dicha dirección de retorno.

    gdb –args /narnia/narnia2 $(python E3.py)

    Como se puede ver en la anterior imagen, la dirección de retorno ha sido sobrescrita con “0x43”s que en ASCII simboliza el valor “C”. Por lo tanto, solamente queda preparar el payload para que ejecute nuestra shell.

    Anteriormente, se ha mostrado como el stack se encuentra estructurado. Por lo que si uno lo piensa bien, para llamar a una función básica como “system” la cual solamente necesita un puntero a una dirección de retorno (El cual no es necesario, porque solamente nos interesa que se ejecute dicha función) y un puntero a una cadena de caracteres donde se encuentre el comando que se quiere ejecutar (Este valor, es el parámetro de la función).

    Entonces, para poder obtener nuestra shell, necesitamos encontrar la dirección de system en el binario y la dirección de una cadena de caracteres que contenga “/bin/sh”.

    Primero para encontrar la dirección de system, se ha empleado gdb introduciendo la ruta del del programa como parámetro.

    Segundo, para obtener la dirección de “/bin/sh” hay varias propuestas. La primero es insertar en nuestro exploit la cadena y obtener la dirección donde comienza la cadena, o la segundo, la librería libc cuenta con su propia cadena “/bin/sh”.

    Esta solución se basa en la segunda propuesta, donde usamos gdb para encontrar la dirección de “/bin/sh”.

    Finalmente, una vez que se ha obtenido la dirección de system y la dirección de “/bin/sh”, falta sustituirlo en el exploit, lanzarlo y obtener la contraseña para el siguiente nivel.

    Narnia 3

    Según el código que se proporciona para superar este nivel, trata de un programa que recibe un único valor como argumento. Este valor es copiado a un buffer de 32 bytes de tamaño a través de la función vulnerable a buffer overflows “strcpy” y es tratado como una ruta de archivo. Una vez que se ha copiado el valor en ifile, el programa comprueba si tiene permisos de escritura para el archivo “/dev/null”. Posteriormente, el programa comprueba si tiene permisos de lectura para leer el archivo, cuya ruta se ha pasado como argumento. Finalmente, si tiene permisos de escritura y lectura, el programa lee 32 bytes del archivo pasado como argumento y lo escribe en “/dev/null”.

    El fichero “/dev/null” es un fichero, el cual todos los datos que se han escrito en él son descartados y no se puede leer su contenido.

    Como se ha explicado anteriormente, para poder superar este nivel, es necesario basarse en utilizar la función vulnerable “strcpy” sobrescribiendo el buffer ofile. En un principio se pensó en utilizar null bytes (“0x00”) para poder separar las rutas de ifile y ofile.

    Sin embargo, el programa ignora los null bytes y lo trata como si la combinación de ifile+ofile se tratase de una única cadena.

    gdb --args /narnia/narnia3 $(python -c 'print

    "/etc/narnia_pass/narnia4"+"\x00"*8+"/tmp/marmeus/a"+"\x00"*2')

    Como se puede ver en la imagen anterior, no existen null bytes dentro de la cadena pasada como parámetro, por lo que los buffers no se han sobrescrito correctamente.

    Para evitar esto, se ha creado una función “padding” que rellena los caracteres restantes para llenar los buffers con “/” (0xf2 en hexadecimal), los cuales a la hora de ser interpretados serán interpretados como una única “/”. La desventaja de este método es que el programa tomará la cadena “////////////////////tmp/marmeus////tmp/marmeus/a” como el archivo para leer y la cadena “////tmp/marmeus/a” como el archivo donde se escribiría la contraseña, debido a las posiciones en memoria de los buffers y al único null byte al final de la cadena. (Se indica el final de una cadena con el valor “0x00”)

    Además, es necesario decir, que el primer ejemplo no se puede llevar a la práctica, puesto que se tendría que crear la ruta “/etc/narnia_pass/narnia4/tmp/marmeus/a” puesto que no se tienen permisos de escritura en la carpeta “narnia_pass”. Por ende, se ha utilizado la carpeta tmp, donde se tienen los permisos necesarios para superar este nivel.

    Ahora bien, para poder leer el archivo “/etc/narnia_passs/narnia4” a través de la ruta “/tmp/marmeus/tmp/marmeus/a”, se emplea el fichero “a” como un link simbólico a “/etc/narnia_pass/narnia4”.

    ln -s /etc/narnia\_pass/narnia4 a

    De esta forma, el programa en vez de leer el fichero “a” leerá el fichero “/etc/narnia_pass/narnia4”. Finalmente, queda crear el archivo “/tmp/marmeus/a”, pues es donde se escribirá la contraseña para el nivel 4.

    El script en python resultante, es el siguiente:

    Que al ejecutarse, se obtiene la contraseña del nivel 4.

    /narnia/narnia3 $(python E4.py)

    Narnia 4

    En este nivel se ve que tenemos un programa el cual sobrescribe todas las variables de entorno a 0. Posteriormente, comprueba si el programa ha sido ejecutado con más de un argumento y copia el primer argumento en *buffer *a través de la función vulnerable strcpy.

    Con la finalidad de sobrescribir el buffer y averiguar cuantos caracteres hacen falta para sobrescribir la dirección de retorno, se ha escrito el siguiente script.

    Al ejecutarlo con GDB se descubre que las Ds (0x44 en hexadecimal) son las que han sobrescrito la dirección de retorno.

    Por consiguiente, se va emplear la técnica de ret2libc, como se pudo ver en** Narnia 2**,obteniendo el siguiente script.

    Sin embargo, debido a que la dirección de exit 0xf7e40800 contiene un null byte (0x00), no se puede emplear.

    Por lo que directamente, se sustituye por cuatro Ds.

    Obteniendo el exploit final y la clave para el siguiente nivel.

    Narnia 5

    Para solucionar este nivel se emplea la técnica de Format String, donde se introducen caracteres especiales que posteriormente son interpretados por snprintf para leer o escribir en determinadas posiciones del stack. (Pronto haré tutoriales más en profundidad sobre explotación binaria)

    Para poder ver los valores que se encuentran en el stack se emplea “%x”. De esta forma, se extrae información del stack y se puede averiguar donde comienza la cadena de caracteres que se le pasa al programa.

    El siguiente script sirve para buscar las As en la memoria del programa.

    Como se puede ver en la siguiente imagen, las As (en hexadecimal 0x41) se encuentran en la primera palabra o WORD en el stack, donde se esta ejecutando snprintf.

    De esta forma, una vez encontradas las As, se pueden sustituir por una dirección de memoria, en este caso, la dirección de i que nos la proporciona el propio programa 0xffffd690.

    Modificando el script anterior, cambiando las As, por la dirección de *i

    • y añadiendo “%1$n”, que significa que trate la primera palabra almacenada en el stack como una dirección, en este caso la dirección de* i*,_ se_ escriba en ella la cantidad de caracteres que se mostraría por pantalla, como si de un printf se tratase.

    Ejecutando el script y el programa, se puede apreciar como en vez de 1 ahora* i* es igual a 4.

    Esto es debido a que “%x” ha impreso la dirección de* i* que es una WORD o 4 bytes de memoria, a lo que cada byte es tratado como si fuese un carácter. Por tanto, para snprint antes de llegar a “%1$n” se han impreso 4 caracteres, que es el 4 que se ve en la imagen anterior.

    Finalmente, como para superar este nivel se requiere sobrescribir la variable *i *con el valor 500, es necesario imprimir los 496 caracteres restantes. Para ello, se hace el uso de “%Xx”, donde la X representa el número de caracteres extra se quiere imprimir. Por lo tanto, si por defecto se imprimen 4 caracteres, X debería de ser igual a los 496 restantes.

    El exploit resultante es el siguiente:

    Que al ejecutarse obtenemos el valor correcto, y la contraseña para el siguiente nivel.

    Narnia 6

    Para superar este nivel se utiliza la técnica ret2libc, pero algunos matices. Primero de todo, el el programa comprueba si se ha ejecutado con 2 argumentos. Si es así, sobrescribe las variables de entorno a 0s y si se han escrito más de 2 argumentos los sobrescribe a 0s.

    Segundo, el programa copia los dos argumentos introducidos por el usuario en los buffers b1 y b2 de 98 bytes cada uno, a través de la función vulnerable strcpy.

    Tercero y último, el programa comprueba que el puntero función fp se encuentra en el stack. Que como se puede ver en la siguiente imagen, esta apuntando a la dirección 0x99bc25ff que a la hora de hacer la operación and con 0xff000000 resulta en 0x80000000 por lo que no coincide con 0xff000000 y el programa ejecuta fb pasando como parámetro el primer argumento introducido por el usuairo.

    Como anteriormente se ha visto, se utilizan la función vulnerable “strcpy”. Por lo tanto, mediante el siguiente script se desea comprobar si se puede sobrescribir dirección almacenada en la variable fp.

    Que como podemos apreciar en la siguiente imagen, la dirección de fp ha sido sobrescrita por Bs.

    No obstante, aunque se reemplace la dirección de fp por la de *system *y se añada la dirección de “/bin/sh”, el exploit no funcionará porque antes de llamar a _fp _ se introduce en el stack la dirección de b1, impidiendo que se salte a la dirección de “/bin/sh”. Por tanto, es necesario que el exploit contenga la cadena “/bin/sh0x00”.

    Para conseguirlo, no valdría con escribir en el buffer “/bin/sh\x00+<Dirsystem>”, debido a que al contener la cadena un _null byte indicando la terminación de la cadena, la dirección de system jamás se copiaría.

    Sin embargo, si se sobrescribe el primer buffer, haciendo un overflow del segundo, se puede escribir la cadena “/bin/sh\x00” en el primer buffer sin necesidad de pasar el valor 0x00 como argumento del programa.

    La siguiente imagen clarifica como primero se ha sobrescrito la dirección de fp y posteriormete el buffer 1 ha sido sobrescrito por Bs con un *null byte *al final.

    Modificando el exploit anterior

    se puede apreciar mediante GDB como se obtiene la cadena “/bin/sh\x00”.

    Y por ende, la shell para conseguir la contraseña del siguiente nivel.

    Narnia 7

    Este nivel al principio puede parecer un poco lioso, pues hay varias funciones a parte de main y bastantes líneas de código. Pero tampoco hace falta conocer mucho del código para saber como funciona el programa.

    La función main que comprueba si se ha introducido al menos un argumento. Si es así, se ejecuta la función vuln que muestra por pantalla las direcciones de memoria de goodfunction, hackedfunction y la variable _ ptrf_. Además, la asigna a ptrf la dirección de goodfunction y realiza una un snprintf antes de llamar a ptrf.

    El objetivo de este nivel es sobrescribir el valor de ptrf por el de la función hackedfunction para ello es necesario utilizar la técnica de format string, que al igual que en el nivel Narnia 5 se tendrá que valer de los valores “%x” y “%n” para realizar dicha tarea.

    Para empezar, se ha escrito el siguiente “.gdbinit” para poder depurar el programa con GDB y poder ver que valores se están escribiendo en la variable buufer.

    Después ejecutamos el siguiente comando para averiguar en que posición de la memoria se encuentran las As.

    gdb --args /narnia/narnia7 $(python -c 'print "AAAA"+"%x"\*10')

    Cambiando las As por la dirección de ptrf, obtenemos el siguiente exploit.

    Que ejecutándolo se obtiene el siguiente resultado.

    Se han impreso 11 caracteres (0xb = 11).

    Ahora bien, de la misma forma que se hizo en el nivel de Narnia 5 para conseguir el valor 500, se tiene que realizar para este nivel. Para ello, se tiene que restar a la dirección hackedfunction menos cuatro.

    Obteniendo el siguiente exploit.

    Que al ejecutarlo nos proporciona una shell para obtener la contraseña del siguiente nivel.

    Narnia 8

    Este nivel es el nivel más difícil de todos, pero no por ello imposible. El problema radica en obtener un buffer overflow y una sobrescritura de la dirección de retorno mediante la copia de valores del vector blah al vector bok mediante el bucle* for*, que ya se detallará más adelante.

    Para depurar el programa se ha empleado el siguiente fichero “.gdbinit”.

    Se ejecuta el programa con los siguientes parámetros.

    gdb --args /narnia/narnia8 AAAAAAAAAAAAAAAAAAAABBBBCCCC

    Como se puede apreciar en las siguientes capturas de pantalla, se van añadiendo carácter a carácter los valores de la cadena de caracteres introducida por el usuario.

    Sin embargo el programa empieza actuar de forma extraña cuando se empieza a sobrescribir la dirección 0xffffd850, que apunta al vector blah.

    Debido a que no termina de obtener los valores que se piden y puede sobrescribir en posiciones de memoria que no se deberían, por lo que el programa puede terminar de forma abrupta.

    Para subsanar este problema, después de llenar el _buffer _ de As, se procede a escribir la dirección del vector blah, obteniendo el siguiente script.

    Que al ejecutarlo frente al programa, obtenemos que se ha producido una violación de segmento producida por escribir cuatro Cs en la dirección de retorno.

    Ahora que ya conocemos todos los valores, mediante la técnica ret2libc podemos obtener nuestra shell, como se puede ver en el siguiente exploit.

    Lamentablemente, aunque este exploit sirva para dentro de GDB, fuera de GDB no funcionará pues: la direcciones entre GDB y una ejecución del programa pueden que no coincidan, las variables de entorno pueden variar y la forma en la que se llama al programa puede variar. Por tanto, es necesario realizar fuerza bruta con las direcciones de BLAH hasta que el exploit funcione. Para ello se ha realizado el siguiente exploit, que a través de probar suerte con una dirección de memoria parecida a la que aparecia en GDB y fuerza bruta se consigue la shell.

    Al ejecutarlo, el script empezará a ejecutar un montón de veces narnia9 con diferentes direcciones de BLAH hasta que una de ellas funcione, mostrandole al usuario la ansiada shell.

    Narnia 9

    Finalmente, se puede dar por concluida la serie de retos de Narnia :D

    Contraseñas

    0) narnia0
    1) efeidiedae
    2) nairiepecu
    3) vaequeezee
    4) thaenohtai
    5) faimahchiy
    6) neezocaeng
    7) ahkiaziphu
    8) mohthuphog
    9) eiL5fealae