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)
s.send(tmp)
print("sent " + str(tmp))
# recieve response
response=s.recv(1024)
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
Theory
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]
^
ptr
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]
^
ptr
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
23anbT\rE