The next step in difficulty for the OverTheWire wargames is Leviathan. From the description:
This wargame doesn’t require any knowledge about programming – just a bit of common sense and some knowledge about basic *nix commands.
Leviathan’s levels are called leviathan0, leviathan1, … etc. and can be accessed on leviathan.labs.overthewire.org through SSH.
To login to the first level use:
Username: leviathan0
Password: leviathan0
Data for the levels can be found in the homedirectories.
Level 0 –> Level 1
There is no information about what you have to do for each level to progress, so you just have to look around.
If you do a ls -la in the home directory, you will see an interesting hidden directory owned by leviathan1:
1 2 3 4 5 6 7 8 |
|
Inside there is a bookmarks.html file with lots of links, so I tried grepping for the word password and wasn’t disappointed:
1 2 |
|
Level 1 –> Level 2
There is a setuid binary in the home folder that asks for a password. Running strings on it didn’t reveal much, except that it uses strcmp..so it compares the input it receives with something..
I solved this by running ltrace on the binary. ltrace is a library call tracer:
ltrace is a program that simply runs the specified command until it exits. It intercepts and records the dynamic library calls which are called by the executed process and the signals which are received by that process. It can also intercept and print the system calls executed by the program.
1 2 3 4 5 6 7 8 9 10 11 |
|
In the ltrace output you can actually see the password that your input is compared with. Give it to the program and you will get a shell as leviathan2:
1 2 3 4 |
|
As in the previous wargame, look in /etc/ for the password:
1 2 |
|
Level 2 –> Level 3
In this level we have another setuid binary named printfile. If you try to read the password file for the next level you just get a message that you an’t have that file:
1 2 |
|
Running ltrace again we see the access system call is being used:
1 2 3 4 5 6 |
|
This is good news, because access is vulnerable to race conditions, as can be seen from its man page:
access() checks whether the calling process can access the file pathname. If pathname is a symbolic link, it is dereferenced.
The check is done using the calling process’s real UID and GID, rather than the effective IDs as is done when actually attempting an operation (e.g., open(2)) on the file. This allows set-user-ID programs to easily determine the invoking user’s authority.
Warning: Using access() 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.
A more detailed description from OWASP:
The window of time between when a file property is checked and when the file is used can be exploited to launch a privilege escalation attack.
File access race conditions, known as time-of-check, time-of-use (TOCTOU) race conditions, occur when:
The program checks a property of a file, referencing the file by name. The program later performs a filesystem operation using the same filename and assumes that the previously-checked property still holds.
The first thing I thought was to create a file and make it a symlink to the password file, but it didn’t work. So I just made a random file for test purposes and used ltrace again to see what is happening when accessing it:
1 2 3 4 5 6 7 8 9 |
|
I have to say, I used help for this one, because it wasn’t clear to me on how to proceed (it’s also where I got stuck when I first attempted this wargame). If access returns successfully, it will cat the file that was passed to it (and it’s actually snprintf that does the cat. Remember from Bandit how cat won’t print files with spaces in their names, unless the spaces are escaped or the filename is surrounded by quotes. I made a new dummy file with a space in its name:
1 2 3 4 5 |
|
Now I ran ltrace again and tried to print this new file:
1 2 3 4 5 6 7 8 9 10 |
|
There is a discrepancy between access, which checks the path of the file, and what cat tries to print, two different files that don’t exist, space and file (because the space isn’t seen as part of the filename, but as a separator between arguments):
cat tries /tmp/baka/space
cat tries file
This is where the symbolic link exploitation part comes in place. It didn’t work before, but if I now create a symlink to the password file and name it space, it will match the first half of the file that cat will try to print:
1 2 3 4 5 |
|
And now I run printfile again:
1 2 3 |
|
Yay! It worked! Because cat first tried to print space, it followed the symlink with the same name and printed the password!
Level 3 –> Level 4
And another setuid binary! Running strings on it revealed it’s a program that gives you a shell if you enter the right password. Along with string names such as do_stuff, nothing and morenothing xD
ltrace keeps helping a lot with these challenges:
1 2 3 4 5 6 7 8 9 10 |
|
The first strcmp doesn’t seem to be used for anything, but the second one is interesting. It compares the given input with..snlprintf?! There is no such function in the C library, so I assumed it’s just a string..and:
1 2 3 4 5 6 7 |
|
Awesome! On to the next level!
Level 4 –> Level 5
There is a hidden directory in the home folder:
1 2 3 4 5 6 7 8 |
|
Inside there’s a program that gives some binary output when run:
1 2 |
|
Convert it to Ascii for the password: Tith4cokei
Level 5 –> Level 6
Another binary! If you run it, it says it can’t find a file:
1 2 |
|
This is a setuid binary, so maybe we can trick it into reading the password file for the leviathan5 user. I made a symlink to the password file with the name of the missing file, and the binary followed it right to the password:
1 2 3 |
|
Level 6 –> Level 7
And yet another binary! This one asks for a 4-digit code:
1 2 |
|
Lookint at the strings, it seems this program will give us a shell if we can find the right code. I was going to use Python again but I found a quick and easy way to bruteforce the code with a very small Bash script:
1 2 3 4 5 6 7 |
|
This script iterates over the range of possible codes and tries each of them as input for the leviathan6 binary:
1 2 3 4 5 6 7 8 |
|
Level 7 –> Level 8
This is the final level, as you can see from the congratulatory note:
1 2 3 4 5 |
|
Better skip over that last line…
1 2 3 4 5 6 7 8 9 |
|