OverTheWire Bandit
The whole of OverTheWire Bandit, levels 0 through 33 — one writeup per level solved, dead ends included.
33 writeups
-
Bandit 0 → 1: getting a foothold
Aug 05, 2026
The one where you just have to get in the door — SSH basics and reading your first file.
-
Bandit 1 → 2: the file called -
Aug 06, 2026
A filename made of one character the shell had already claimed.
-
Bandit 2 → 3: spaces in the filename
Aug 06, 2026
Whitespace is an argument separator before it's a character in a name.
-
Bandit 3 → 4: the empty directory that isn't
Aug 06, 2026
ls told me there was nothing there. ls was being polite.
-
Bandit 4 → 5: file before cat
Aug 06, 2026
Ten files, one of them readable — and a command that tells you which without printing any of them.
-
Bandit 5 → 6: three properties, one file
Aug 06, 2026
Twenty directories, one file that matches the description — and a lesson about which filter to reach for.
-
Bandit 6 → 7: searching the whole filesystem, quietly
Aug 06, 2026
The level where ls stops helping — three properties that turn out to be a search query, and the redirect that clears the noise out of the answer.
-
Bandit 7 → 8: don't read the file, search it
Aug 06, 2026
Once a file is bigger than the screen, cat stops being a way of looking at things.
-
Bandit 8 → 9: the line that only happens once
Aug 07, 2026
A file full of passwords where the only thing marking the real one is that it isn't repeated.
-
Bandit 9 → 10: readable text in a binary file
Aug 07, 2026
Most of the file isn't text. The password is, and it's wearing a marker.
-
Bandit 10 → 11: base64 is not a lock
Aug 07, 2026
Encoded, not encrypted — and one flag undoes it.
-
Bandit 11 → 12: rot13, and what `tr` actually does
Aug 07, 2026
Every letter shifted thirteen places, by a command whose shape makes it look like it is doing something it isn't.
-
Bandit 12 → 13: the hexdump matryoshka
Aug 07, 2026
A hexdump wrapping a stack of compressed files, each a different format, inside a home directory you're not allowed to write to. `file` is what turns the guessing game into a loop.
-
Bandit 13 → 14: the key that wouldn't open the door next to it
Aug 08, 2026
An SSH key instead of a password, a localhost that refused the intended one-liner, and the reason scp is on the level's command list.
-
Bandit 14 → 15: the port that waits for you to talk first
Aug 15, 2026
A raw TCP connection has no prompt, no Password: line, and looks exactly like a hang. It isn't.
-
Bandit 15 → 16: ask the box which tools speak TLS
Aug 15, 2026
The level names ten tools and links two articles that don't get you to the command. The box documents all ten, so ten greps against the man pages produce the shortlist in about a minute.
-
Bandit 16 → 17: when the server says wrong, believe it
Aug 15, 2026
Five listening ports, two that speak TLS, and one server that's the only thing in the level capable of telling you your password is wrong.
-
Bandit 17 → 18: which side of the diff
Aug 16, 2026
One changed line between two files. diff finds it in a second — then you have to know which of the two lines it hands back is yours.
-
Bandit 18 → 19: a shell that quits before you can type
Aug 16, 2026
The login isn't refused — it succeeds, and then the shell hangs itself. So don't ask for a shell.
-
Bandit 19 → 20: a command is not a menu
Aug 17, 2026
A setuid binary that runs anything you hand it as someone else — and the difference between an example invocation and a menu of allowed ones.
-
Bandit 20 → 21: be the other side
Aug 19, 2026
A setuid binary that connects to you, not the other way round — and the discovery that the side which waits still has to talk first.
-
Bandit 21 → 22: it already ran
Aug 20, 2026
A cron job dumps the next password into /tmp every minute — the whole level is realising you read the result, not run the script.
-
Bandit 22 → 23: the filename you can compute
Aug 20, 2026
Another cron job, but this time the password lands in a /tmp file whose name is an md5 of a username — so the whole level is realising you can recompute that name yourself.
-
Bandit 23 → 24: hand the privileged user your script
Aug 21, 2026
A cron job runs whatever script you drop in a spool dir — but it runs it as bandit24. So the level isn't reading the password, it's writing the few lines that make bandit24 read it for you. Two permission traps hide in those lines.
-
Bandit 24 → 25: ten thousand pincodes, and a grep that lied
Aug 22, 2026
The pincode has 10000 possibilities and one daemon, so you fire all of them down a single connection. The loop is the easy part — the trap was a grep matching the word 'correct' hiding inside every 'Wrong!'
-
Bandit 25 → 26: the login shell that only runs more
Aug 22, 2026
bandit26's shell isn't bash — it's a script that execs more and exits, so the session dies before you can type. You keep the pager alive by shrinking the terminal until it has to pause, open vi from inside it, and climb out to bash. The trap: every escape defaults back to showtext unless you point it at a real shell first.
-
Bandit 26 → 27: a setuid binary that runs as someone else
Aug 22, 2026
bandit26's home has bandit27-do — a setuid binary owned by bandit27 that runs any command you hand it as bandit27. Aim it at the password file you can't read yourself. The trap: it takes a command, not a username.
-
Bandit 27 → 28: localhost is wherever you're standing
Aug 24, 2026
The level hands you a clone URL pointing at localhost:2220 and then tells you to run it from your own machine. Both halves are true, and together they're a trap — localhost stops meaning the game server the moment you step off it.
-
Bandit 28 → 29: the file was redacted, the history wasn't
Aug 24, 2026
The clone lands a README with the password field blanked out, which reads like a dead end. It isn't — a later commit did the blanking, and the commit before it is still sitting in the repository you just downloaded.
-
Bandit 29 → 30: the branches you weren't shown
Aug 25, 2026
The README says there are no passwords in production, and `git branch` says there's only one branch. Both are true, and neither means what it looks like — a clone builds exactly one local branch and leaves the rest sitting there as refs you have to ask for by name.
-
Bandit 30 → 31: the ref that isn't a file
Aug 25, 2026
One commit, one branch, a README that calls itself empty — and an empty refs/tags/ directory that turns out to be the confirmation rather than the dead end. A clone brings down tags by default and then packs them where ls can't see them.
-
Bandit 31 → 32: the file git kept pretending to add
Aug 31, 2026
Fifth git level, and the first one that asks you to write instead of read. The file gets created, added and committed, and the push answers 'Everything up-to-date' — because a one-line .gitignore that shipped with the repo had been eating it the whole way through.
-
Bandit 32 → 33: the shell that uppercases you
Aug 31, 2026
The login shell shifts everything you type to uppercase, so no command survives contact with the keyboard. The way out is a lowercase string the shell is already holding — $0, the name of the shell itself.