Klaus' Log

Mo 01 Oktober 2018

Overthewire vortex Wargame - Level 0 and 1

Posted by Klaus Eisentraut in ctf   

For a long time, I wanted to do the vortex Wargame. I've recently started it and want to document my progress here.

Level #0

Level #0 is quite easy and only the warm-up. You have to read four unsigned 32-bit integers, sum them and send the result back in order to get the SSH password for level #1. I think my python solution is pretty straight-forward and does not require any extra explanation:

#!/usr/bin/env python3

import socket
import struct

with socket.create_connection( ("vortex.labs.overthewire.org", 5842)) as s:
    total = 0
    for i in range(0,4):
        # recieve 4 bytes and read them as unsigned little-endian int
        tmp = s.recv(4)
        print("recieved " + str(tmp))
        tmp = struct.unpack("<I", tmp)[0]
        total = (total + tmp) % (2**32)
        print("read %0x, sum now %0x" % (tmp, total))

    # sent back total sum
    tmp = struct.pack("<I", total)
    print("sent " + str(tmp))

    # recieve response
    print("recieved " + str(response))

If you run it, you'll see an output like

$ python ~/vortex/00/00.py 
recieved b'\x8d\xcc\xb8k'
read 6bb8cc8d, sum now 6bb8cc8d
recieved b'\x9c\xcb`r'
read 7260cb9c, sum now de199829
recieved b'\xe2\x00\xbf\x06'
read 6bf00e2, sum now e4d8990b
recieved b'_\x06~z'
read 7a7e065f, sum now 5f569f6a
sent b'j\x9fV_'
recieved b'Username: vortex1 Password: Gq#qu3bF3'

Level #1


Level #1 was a little confusing at first. After looking at the provided code, it immediately was clear to me that we will need to set the pointer ptr to something like 0xca...... in order to win. However, I didn't really get what the while loop is doing. This is kind of very strange code which you probably never use in real life.

However, after sitting down and thinking about what the code does, the solution was quite simple: As we have three local variables buf, ptr and x which are allocated on the stack, the address of ptr is placed just immediately before the address of ptr. One caveat is that the vortex machine is 32bit (!) little-endian, so we are dealing with 4 byte integers and pointers here.

[x3 x2 x1 x0][ptr3 ptr2 ptr1 ptr0][buf0 buf1 buf2 buf3 ... buf255 buf256 buf257 ... buf511]

So we can send 256 backslashes, then ptr will be decreased 256 times and points to the beginning of the buf array afterwards. If we send 1 more backslash, ptr underflows the array buf and points to the most significant byte of itself. After sending 257 backslashes, the situation looks like the following:

[x3 x2 x1 x0][ptr3 ptr2 ptr1 ptr0][buf0 buf1 buf2 buf3 ... buf255 buf256 buf257 ... buf511]

If we now send an \xca byte, it will be written to the location where ptr points to. This happens to be the most significant value of ptr and pointer has now a value of 0xca.......

[x3 x2 x1 x0][ptr3 ptr2 ptr1 0xca][buf0 buf1 buf2 buf3 ... buf255 buf256 buf257 ... buf511]

Afterwards, ptr will be increased by one, too, but this likely won't change the most significant byte. Then, the macro e() is called and as ptr has now a correct value, we get a shell!

Real world

So far, the theory, but - as usual - real world turned out to be more complicated. After struggling with this for quite some time, I googled other solutions, but all of them had a value of 257 backslashes, still I couldn't get it working.

Let's go back to the declaration of the local variables again:

unsigned char buf[512];
unsigned char *ptr = buf + (sizeof(buf)/2);
unsigned int x;

The reason it didn't work for me is that the compiler code is optimized. Compilers sometimes put unused space in between local stack variables in order to increase performance. Also, they might change the order in which the local variables reside on the stack. So we need to carefully take a look again and figure out the alignment.

I downloaded the vortex1 executable and dissassembled it with objdump -S -M intel vortex1. The beginning of the main function is rather boring, it uses the typical two lines of function intro which setup the new stack and then immediately saves the value of two registers esi and ebx in order to restore the values of those at the end of the main function.

080485c0 <main>:
 80485c0:   55                      push   ebp
 80485c1:   89 e5                   mov    ebp,esp
 80485c3:   56                      push   esi
 80485c4:   53                      push   ebx

Then, esp is rounded down to the next 16 byte boundary, presumably for performance reasons. A value of 0x220 is subtracted from esp, indicating that the local variables take 544 bytes on the stack:

 80485c5:   83 e4 f0                and    esp,0xfffffff0
 80485c8:   81 ec 20 02 00 00       sub    esp,0x220

If we compare this to the local variables, then one would expect to have a stack size of only 512 bytes for buf and 2x4=8 bytes for the integer and the pointer ptr. This proofs that our theory about variable alignment (or reordering) is actually correct!

We still need to figure out what the relative offsets of those variables relative to each other are. The next few lines which are the assembly resulting from the code unsigned char *ptr = buf + (sizeof(buf)/2); are telling us:

 80485dd:   8d 44 24 1c             lea    eax,[esp+0x1c]
 80485e1:   05 00 01 00 00          add    eax,0x100
 80485e6:   89 44 24 14             mov    DWORD PTR [esp+0x14],eax

The value of esp+0x14 is overwritten with the value stored in esp+0x1c increased by 0x100 or 256. Therefore, we can deduce that buf is located at esp+0x1c and ptr is located at esp+0x14. The offset between ptr and buf is 0x1c-0x14 = 8 bytes and not 4 bytes as calculated before! Our first picture was wrong, the stack memory actually looks like this:

[ptr3 ptr2 ptr1 ptr0][??? ??? ??? ???][buf0 buf1 buf2 buf3 ... buf255 buf256 buf257 ... buf511]

So the correct solution is just adding 4 more backslashes to the 257 from above, resulting in 261.

Putting all together

I was a little confused what to do with the crappy /bin/sh shell you get after solving the challenge. While looking around on the system, I found the folder /etc/vortex_pass/ which contains the passwords of each user.

The hint You may need to consider how bash handles EOF. made it easy to come up with the final exploit:

vortex1@vortex:~$ python -c 'import sys;sys.stdout.write("\\"*261+"\xca"*2)' > /tmp/klaus ; cat /tmp/klaus - | /vortex/vortex1
cat /etc/vortex_pass/vortex2