OverTheWire Bandit: Level 19 to Level 20
The Goal
Use a setuid binary in the home directory to read the password file for bandit20 — a file that bandit19 cannot access directly.
What I Did
Listed the home directory and found a binary called bandit20-do. Ran it without arguments first as the level suggested:
1
2
3
bandit19@bandit:~$ ./bandit20-do
Run a command as another user.
Example: ./bandit20-do whoami
Tested it with whoami to confirm who it runs as:
1
2
bandit19@bandit:~$ ./bandit20-do whoami
bandit20
Confirmed — the binary runs commands as bandit20. Tried a few wrong approaches first:
1
2
3
./bandit20-do bandit20 # tried passing username as argument — wrong
./bandit20-do whoami | cat # piping doesn't help here
./bandit20-do whoami "cat bandit20" # passing multiple commands as one string — wrong
Then used the full path to the password file:
1
2
bandit19@bandit:~$ ./bandit20-do cat /etc/bandit_pass/bandit20
4pIjcunZ0fK2vmp3IwfG8Vf7VhxD6pOA
Verified the setuid bit with ls -l:
1
2
bandit19@bandit:~$ ls -l bandit20-do
-rwsr-x--- 1 bandit20 bandit19 14880 Jun 24 14:59 bandit20-do
The s in position 4 (rws) where the execute x would normally be confirms the setuid bit is set.
What the setuid Bit Does
Normally when you run a program, it runs with your own user’s privileges. The setuid bit changes this — if set on a binary, the program runs with the privileges of the file’s owner, not the user who executed it.
In this case:
- The binary is owned by
bandit20 - The setuid bit is set
- So when bandit19 runs
./bandit20-do, the process runs as bandit20
This means any command passed to bandit20-do executes with bandit20’s privileges — including reading /etc/bandit_pass/bandit20, which bandit19 cannot access directly.
How to Identify setuid Binaries
Using ls -l:
1
2
ls -l bandit20-do
-rwsr-x--- 1 bandit20 bandit19 14880 Jun 24 14:59 bandit20-do
The s in the owner execute position (rws) indicates setuid is set. If the owner doesn’t have execute permission, it shows as S (uppercase) instead.
Using find to locate all setuid binaries on a system:
1
find / -perm -4000 2>/dev/null
This is one of the first commands run during penetration testing after gaining initial access — looking for setuid binaries that might be exploitable to escalate privileges.
Real World Significance
setuid is how Linux allows specific programs to do things regular users can’t. The ping command needs to send raw network packets — a root-only privilege — so it has the setuid bit set with root as owner. Any user can run ping but the process briefly runs as root just long enough to do its job.
The security risk: if a setuid binary has a vulnerability like a buffer overflow, an attacker who can exploit it inherits the owner’s privileges. A vulnerable setuid root binary means full root compromise from a normal user account. This is one of the most common privilege escalation vectors in real penetration tests and CTF challenges.
What I Learned
setuid lets a binary run as its owner regardless of who executes it. The binary acts as a bridge between privilege levels.
The s in ls -l output identifies setuid. Lowercase s means setuid is set and the owner has execute permission. Uppercase S means setuid is set but the owner does not have execute — which is usually a misconfiguration.
Full paths avoid ambiguity. Instead of navigating to /etc/bandit_pass/ and then running cat, passing the full path as a single argument to the binary is cleaner and works regardless of your current directory.
Commands Used
| Command | What it did |
|---|---|
./bandit20-do | Showed usage — runs commands as another user |
./bandit20-do whoami | Confirmed the binary runs as bandit20 |
./bandit20-do cat /etc/bandit_pass/bandit20 | Read the password file as bandit20 |
ls -l bandit20-do | Confirmed the setuid bit with the s in permissions |