CTF · Wargames
Step 99. Bandit 16~20 — First Encounter with setuid
Level 2 — Introduction to Security and Attack Skill Basics | Difficulty ★★★☆☆ | Estimated time: 3 hours
Prerequisites: you’ve finished the find conditions and SUID concepts from Step 97, and the nc·openssl·key connections from Step 98. You have the Bandit 15→16 password.
- What you need: an SSH connection environment, your password record, and a local Linux/WSL setup for experiments.
- ⚠️ All exercises in this chapter are for your own lab and legal platforms only. Applying them to unauthorized systems is a crime.
- About the legal practice ground: OverTheWire Bandit is an official attack-practice platform. Do setuid experiments only on that server and in practice folders on your own computer, and apply permission experiments only to files you own. Clean up experiment files as soon as you’re done.
Bandit so far has been a game of "find and read." Starting today, a concept of a different class appears — a program that runs with someone else’s privileges. Let’s start with one question. There’s a file my account can absolutely never open. And there’s a strange program sitting nearby. When I run this program, for the moment it executes, I become that file’s owner. If I tell this program "read that file for me"? It reads it. This is setuid — the alpha and omega of Linux privilege escalation.
1. Learning Objectives
By the end of this chapter, you will be able to:
- Find open services with a port-range scan and check whether they use TLS
- Read only the differences between two files with
diff - Run a command directly without a shell using
ssh address "command" - Recognize setuid files by the
smarker inls -land prove the privilege swap with anidexperiment - Connect an nc listener and client across two terminals to make "the other side come to me"
2. Background Knowledge — Today’s Tools and Concepts
Today’s Tools at a Glance
| Category | Details |
|---|---|
| Language/environment | Linux shell (Bandit server + local WSL) |
| Today’s commands | nmap -p range, diff, ssh ... "command", id, chmod u+s, nc -l |
| Concepts needed | the setuid bit, uid/gid, privilege escalation, the listener-client structure |
| Today’s artifact | Bandit 16→21 password chain + a setuid concept document |
2-1. How setuid Works — A Program Wearing a Privilege Mask
Look at a file in ls -l whose owner-execute slot shows s instead of x.
-rwsr-xr-x 1 bandit20 bandit19 ... bandit20-do
That s is the setuid bit. Its meaning is this — "when this file is executed, no matter who runs it, the process runs with the owner’s privileges."
Why does such a mechanism exist? The classic example is passwd. Changing my password requires touching a system file only root can write to. Since we can’t hand root to every regular user, a safety pin was attached: "root privileges only for the moment passwd runs." The problem is when a program wearing that safety pin has a hole — if you can make it execute an arbitrary command through the hole, that command runs with the owner’s privileges. The SUID you met in Step 97 as a "list it out" concept becomes an actual rank ladder today.
2-2. diff — Seeing Only the Difference Between Two Files
diff file1 file2 compares two files line by line and shows only the differing parts. What changed in a config file before and after an update? Which line in two password lists is the new one? You use it whenever "the difference" is itself the information.
2-3. ssh’s Hidden Talent — Passing a Command Along with the Connection
If you append a command to the connection in the form ssh user@address command, it executes just that command without entering a shell and brings back the result. It’s an emergency exit that bypasses finicky accounts that kick you out the moment you log in — and it’s also the basic form of automation scripts.
2-4. Listener and Client — A Meeting I Set Up Myself
In Step 98 you saw nc’s two faces — the connecting side and the waiting side (-l). Today, the same person (you) plays both at once: waiting in one terminal, and making a program connect to you from another terminal. This structure — "I lay out the board and make the other side walk onto it" — is the skeleton of a reverse shell (a technique where the attack target connects back to the attacker).
3. Follow Along
3-1. Level 16 → 17 — Finding the Open Port Yourself
Goal: "find the open port between 31000~32000 on localhost, connect via SSL, and submit the key."
Input (on the server, Screen example)
nmap -p 31000-32000 localhost
We measured this scan on our own computer — with an nc server standing on port 31500, we scanned 31490~31510 (measured 2026-09-09, WSL, Nmap 7.94):
PORT STATE SERVICE
31490/tcp closed unknown
...
31500/tcp open unknown
31501/tcp closed unknown
...
Nmap done: 1 IP address (1 host up) scanned in 0.10 seconds
How to read it: only one of the 21 is open. In Bandit, a few open ports come up, and you must test each one with openssl s_client to find which speaks SSL (Step 98’s technique). A three-step combo: scan → verify → converse.
3-2. Level 17 → 18 — Finding the Changed Line with diff
You have passwords.old and passwords.new, and the one changed line is the answer.
Input (on the server, Screen example)
diff passwords.old passwords.new
Local reproduction (measured 2026-09-09, WSL):
printf 'aaaanbbbbnoldpass_w0rdnddddn' > passwords.old
printf 'aaaanbbbbnNEWPASS_XYZnddddn' > passwords.new
diff passwords.old passwords.new
3c3
< oldpass_w0rd
---
> NEWPASS_XYZ
How to read it: 3c3 means "line 3 changed (change)." < is the left (old) file’s content; > is the right (new) file’s. That single changed line is the answer. diff is an endlessly useful tool — tracking config changes, code review, and comparing breach traces ("the original vs. now").
3-3. Level 18 → 19 — An Account That Kicks You Out on Login
This account bounces you instantly upon connecting — an exit is planted in .bashrc (the settings script that runs automatically at login).
Input (the way without entering a shell, Screen example)
ssh bandit18@bandit.labs.overthewire.org -p 2220 "cat readme"
It asks for the password, and once entered, it prints only the readme’s contents with no shell prompt and closes the connection.
How to read it: because the command appended to the connection went straight through without passing a login shell, it dodged the exit command in .bashrc. The sense that "things that run automatically" alter the flow — this is exactly the preview of the next unit (cron).
3-4. Level 19 → 20 — Running a setuid Binary
Input (on the server, Screen example)
ls -l
./bandit20-do
./bandit20-do id
./bandit20-do cat /etc/bandit_pass/bandit20
Screen example (the key scene):
$ id
uid=11019(bandit19) gid=11019(bandit19) groups=11019(bandit19)
$ ./bandit20-do id
uid=11020(bandit20) gid=11019(bandit19) groups=11019(bandit19)
How to read it: passing through the program changed the uid to bandit20. The program reads on your behalf a file your own privileges can’t read. These three lines are a scale model of privilege escalation.
3-5. Observing the setuid Bit — On Your Own Computer
Without changing the system, we set the bit only on a file we own and observe the marker (measured 2026-09-09, WSL):
cp /bin/echo myecho
ls -l myecho
chmod u+s myecho
ls -l myecho
find . -perm /4000 -type f
Output (measured 2026-09-09):
-rwxr-xr-x 1 root root 35208 Sep 9 14:31 myecho
-rwsr-xr-x 1 root root 35208 Sep 9 14:31 myecho
./myecho
How to read it: one chmod u+s flipped the owner-execute slot from x to s, and Step 97’s reconnaissance command (find -perm /4000) pinpointed this file exactly. The making-side command and the finding-side command linked into one loop. Delete this file when the experiment is over.
3-6. Level 20 → 21 — Laying Out the Board and Waiting
Goal: a setuid program called suconnect will "connect to a port you specify, send the current password, and if it’s correct, send back the next password."
Input (on the server, Terminal 1 — I’m the server, Screen example)
nc -l -p 1234
Input (Terminal 2)
./suconnect 1234
A connection arrives at Terminal 1. Type the current password, and after verification the next password arrives at Terminal 1.
How to read it: the connecting side (the setuid program) and the waiting side (my nc) met. It’s the same structure as Step 98’s local nc experiment (measured 2026-09-09), except this time the connecting party is "a program with higher privileges." An experience of holding both ends of a communication at once.
3-7. Dissecting id and Privileges — Who Am I?
Let’s take apart today’s key command, id (measured 2026-09-09, WSL — our experiment account is root, so the output shows root):
id
uid=0(root) gid=0(root) groups=0(root)
How to read it: uid is the user number, gid the primary group number, and groups every group you belong to. Linux’s permission decisions happen by these numbers, not names. You can also confirm root’s uid is 0 — the king of the system is number 0.
Why: now you can see the substance of what a setuid program does — pass through it, and the process’s uid is swapped for the file owner’s number. The grand phrase "privilege escalation" is really the swapping of a single number.
4. Missions & Exercises
Mission — The Bandit 16→21 Chain and a setuid Document
- Complete the chain through bandit20 and record each level in write-up format
- Organize the setuid verification procedure — find the
sinls -l→ list them withfind / -perm /4000 -type f 2>/dev/null - Reproduce the observation experiment from 3-5 on your own computer and delete the experiment file immediately
- Write
setuid.mdin your wiki — the principle / why it exists (the passwd example) / why it’s dangerous / the defender’s countermeasure (list monitoring) - Define "privilege escalation" in your own words in three sentences and put it at the top of the document
Exercises
Exercise 1. Explain the meaning of s in -rwsr-xr-x, and what happens to the process when a program with this bit set is executed.
Exercise 2. What do 3c3, <, and > in diff output each mean?
Exercise 3. Explain the principle by which ssh ... "cat readme" dodges the exit in .bashrc on the Level 18 account.
Exercise 4. On some server, the cat program is configured as root-owned + setuid. What can a regular user do, and why is this dangerous?
Answers & completion criteria · expand/collapse
5. Model Answers & Completion Criteria
Mission Model Answer
The skeleton of the chain (on-server commands are a Screen example):
nmap -p 31000-32000 localhost # L16→17 (open port candidates)
openssl s_client -connect localhost:<open-port> # verify the SSL port, then submit the key
diff passwords.old passwords.new # L17→18
ssh bandit18@bandit.labs.overthewire.org -p 2220 "cat readme" # L18→19
./bandit20-do cat /etc/bandit_pass/bandit20 # L19→20
nc -l -p 1234 (other terminal) ./suconnect 1234 # L20→21
How to verify: ① did you confirm with your own eyes that the uid changed in ./bandit20-do id‘s output? ② does setuid.md contain both "why it exists" and "why it’s dangerous" — with only one, your understanding is half-baked. ③ did you delete the local experiment file (myecho)?
Exercise Answers
Answer 1. s is the setuid bit; when this file is executed, no matter who runs it, the process’s effective user number (uid) is swapped for the file owner’s number. In other words, the program runs with the owner’s privileges (see the id output comparison in section 3-4).
Answer 2. 3c3 means "the left file’s line 3 changed (change) into the right file’s line 3"; < is the left file’s content and > is the right file’s content (see the local measured output from 2026-09-09).
Answer 3. .bashrc runs when an interactive login shell starts. ssh address "command" runs the command directly without entering a shell, so no interactive shell comes up — and therefore the exit in .bashrc never executes.
Answer 4. They can read every file on the system with root privileges — including /etc/shadow (all users’ password hashes). cat is a tool that "reads the file given as an argument," and setuid makes that reading happen with root privileges. The danger of setuid comes not from the program’s function but from "high privileges × input I can manipulate."
Completion Criteria Checklist
- [ ] I can find open ports with
nmap -p range address - [ ] I can read the setuid bit (
s) inls -loutput - [ ] I can prove a privilege swap by comparing
idbefore and after - [ ] I can read diff output (line numbers,
<,>) - [ ] I can run non-interactive commands in the
ssh ... "command"form - [ ] I can connect an nc listener and client across two terminals
- [ ] Mission: I completed the chain and wrote setuid.md
6. Common Pitfalls & Fixes
Wall 1. nmap shows every port as closed
Symptom (measured 2026-09-09 form):
PORT STATE SERVICE
31000/tcp closed unknown
... (all closed)
Cause: you mistyped the target host (it must be localhost), fat-fingered the range, or you’re typing on your own computer.
Fix: re-check the address and range in nmap -p 31000-32000 localhost, and confirm you’re typing inside the Bandit server. You must be inside the server to see the server’s ports.
Wall 2. I connected to the SSL port but it stays silent when I submit the key
Symptom: connected with s_client, but no conversation happens.
Cause: you connected to a non-SSL port, or the value you’re submitting (the previous level’s key) is wrong.
Fix: if several ports are open, test them one by one. The port where a conversation is established is the answer — this wall appears when you skip the "verify" step of "scan → verify → converse."
Wall 3. Connecting to bandit18 bounces me before I can type anything
Symptom (Screen example):
Byebye !
Connection to bandit.labs.overthewire.org closed.
Cause: the exit in .bashrc — the very intent of the problem.
Fix: use the "command along with the connection" form from 3-3: ssh ... "cat readme" — don’t forget to wrap the command in quotes.
Wall 4. I ran suconnect but nothing arrives at nc
Symptom: the two terminals don’t know each other.
Cause: the port numbers differ between the two sides, or you didn’t launch nc -l first (the waiting side goes first!), or you’re running from the wrong level’s home.
Fix: check the order — ① start waiting with nc -l -p port → ② ./suconnect port from another terminal → ③ same port number. It’s localhost, so getting just these three right is enough.
Wall 5. I made a setuid file but find can’t find it
Symptom: you ran chmod u+s, but it doesn’t appear in find . -perm /4000.
Cause: it’s not an executable (missing the x bit), or you’re searching in a different folder.
Fix: check the -rws marker with ls -l first (the s should be visible, as in the section 3-5 measurement). If the bit is set but it still doesn’t appear, check your search path.
7. Summary
Today’s Concepts
| Concept | One-line explanation |
|---|---|
| setuid | a bit that makes a program run with the owner’s privileges — the s in ls -l |
| uid / gid | the user/group numbers used in permission decisions (root is 0) |
| Privilege escalation | borrowing privileges that aren’t yours to climb higher |
| Non-interactive ssh | ssh ... "command", running a command directly without a shell |
| Listener structure | a communication design where I wait and make the other side come |
Today’s Commands
| Command | What it does |
|---|---|
nmap -p start-end address |
port-range scan |
diff A B |
see only the difference between two files |
ssh address "command" |
run a command directly, no shell |
id |
check current uid/gid/groups |
chmod u+s file |
set the setuid bit (your files only) |
find / -perm /4000 -type f 2>/dev/null |
list setuid files |
nc -l -p port |
become a waiting server |
An Instinct More Important Than Commands
From now on, whenever you look at a program or script, carry this one question — "whose privileges does this run with?" If I run it, my privileges; if setuid, the owner’s privileges; if an automated job runs it, that job owner’s privileges. And the risk-assessment formula: high privileges times manipulable input equals danger. Today, for the first time, you held "privileges that aren’t yours" in your hand. Now that you know both the making-side command (chmod u+s) and the finding-side command (find -perm /4000) for this ladder, grow a defender’s eye alongside.
Once every box is checked, Step 99 is complete.
ONE STEP FURTHER
Finished this lesson?
Check the completion criteria, then mark your progress.