~$ skillshelf
← all writeups

OverTheWire Bandit

The whole of OverTheWire Bandit, levels 0 through 33 — one writeup per level solved, dead ends included.

33 writeups

  1. Bandit 0 → 1: getting a foothold

    The one where you just have to get in the door — SSH basics and reading your first file.

  2. Bandit 1 → 2: the file called -

    A filename made of one character the shell had already claimed.

  3. Bandit 2 → 3: spaces in the filename

    Whitespace is an argument separator before it's a character in a name.

  4. Bandit 3 → 4: the empty directory that isn't

    ls told me there was nothing there. ls was being polite.

  5. Bandit 4 → 5: file before cat

    Ten files, one of them readable — and a command that tells you which without printing any of them.

  6. Bandit 5 → 6: three properties, one file

    Twenty directories, one file that matches the description — and a lesson about which filter to reach for.

  7. Bandit 6 → 7: searching the whole filesystem, quietly

    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.

  8. Bandit 7 → 8: don't read the file, search it

    Once a file is bigger than the screen, cat stops being a way of looking at things.

  9. Bandit 8 → 9: the line that only happens once

    A file full of passwords where the only thing marking the real one is that it isn't repeated.

  10. Bandit 9 → 10: readable text in a binary file

    Most of the file isn't text. The password is, and it's wearing a marker.

  11. Bandit 10 → 11: base64 is not a lock

    Encoded, not encrypted — and one flag undoes it.

  12. Bandit 11 → 12: rot13, and what `tr` actually does

    Every letter shifted thirteen places, by a command whose shape makes it look like it is doing something it isn't.

  13. Bandit 12 → 13: the hexdump matryoshka

    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.

  14. Bandit 13 → 14: the key that wouldn't open the door next to it

    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.

  15. Bandit 14 → 15: the port that waits for you to talk first

    A raw TCP connection has no prompt, no Password: line, and looks exactly like a hang. It isn't.

  16. Bandit 15 → 16: ask the box which tools speak TLS

    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.

  17. Bandit 16 → 17: when the server says wrong, believe it

    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.

  18. Bandit 17 → 18: which side of the diff

    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.

  19. Bandit 18 → 19: a shell that quits before you can type

    The login isn't refused — it succeeds, and then the shell hangs itself. So don't ask for a shell.

  20. Bandit 19 → 20: a command is not a menu

    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.

  21. Bandit 20 → 21: be the other side

    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.

  22. Bandit 21 → 22: it already ran

    A cron job dumps the next password into /tmp every minute — the whole level is realising you read the result, not run the script.

  23. Bandit 22 → 23: the filename you can compute

    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.

  24. Bandit 23 → 24: hand the privileged user your script

    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.

  25. Bandit 24 → 25: ten thousand pincodes, and a grep that lied

    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!'

  26. Bandit 25 → 26: the login shell that only runs more

    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.

  27. Bandit 26 → 27: a setuid binary that runs as someone else

    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.

  28. Bandit 27 → 28: localhost is wherever you're standing

    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.

  29. Bandit 28 → 29: the file was redacted, the history wasn't

    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.

  30. Bandit 29 → 30: the branches you weren't shown

    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.

  31. Bandit 30 → 31: the ref that isn't a file

    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.

  32. Bandit 31 → 32: the file git kept pretending to add

    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.

  33. Bandit 32 → 33: the shell that uppercases you

    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.