November 2, 2026
OverTheWire Bandit Walkthrough: A Complete 11-Lab Guide (Level 0β33)
A detailed guide to solving all 34 Bandit levels, organised into 11 labs.

By Gopal Rajguru
7 min read
If you're new to security, Bandit is a good place to start. It's not very exciting but it does teach you the fundamentals. There's no CVE chaining, no exploit development and no memory corruption. However, you learn how to correctly interpret file permissions, when to use find rather than ls, why SUID binaries are dangerous, and how git will never truly delete anything.
This guide will walk you through all 34 levels, split into 11 labs. Connect using ssh bandit0@bandit.labs.overthewire.org -p 2220 with password: bandit0. All passwords are randomly generated on per-account basis. This means that you won't find any passwords in this article. Any user who posts a password has simply copied it from another user's session and by the time you read it, it'll be out of date. You'll have to generate your own password.
Lab 1: SSH Access & Basic File Handling
Levels 0 β 3
The first few levels are relatively simple. They're mainly to get you used to some quirks of the shell. Level 0 is just to prove you can connect.
Level 1 is the first 'wait what?' level. The password file is named β and if you try to run cat β you'll get stuck in an infinite loop. This is because β is a special flag for most Unix tools. This means that we need to give it a full path:
cat ./-cat ./-Level 2's file is spaces in this filename β quote it and move on:
cat "spaces in this filename"cat "spaces in this filename"Level 3 hides the file as a dotfile, invisible to a plain ls:
cd inhere
ls -la
cat .hiddencd inhere
ls -la
cat .hiddenWhat this lab actually teaches: shell argument parsing and dotfile visibility β two things that will keep tripping you up on real systems if you don't internalize them now.
Lab 2: File Identification & Search
Levels 4 β 6
This is where Bandit starts getting fun.
Level 4 has 10 files. 9 of them are misleading. The 1 file that is a text file can be identified using the file command:
cd inhere
file ./*cd inhere
file ./*Find the file that returns ASCII text.
Level 5 has the file hidden in a complex directory structure. The file is human readable, 1033 bytes and not executable. We can use the find command and pass in the 3 criteria:
find . -type f ! -executable -size 1033cfind . -type f ! -executable -size 1033cLevel 6 has no local clues. The file can be anywhere on the filesystem. We know that the file is owned by bandit7, in the bandit6 group and is 33 bytes:
find / -user bandit7 -group bandit6 -size 33c 2>/dev/nullfind / -user bandit7 -group bandit6 -size 33c 2>/dev/nullThe 2>/dev/null is important! Otherwise you'll get a tonne of Permission denied errors and your one correct result will be lost.
What you've learnt: How to search for files based on their content.
Lab 3: Text Processing & Encoding
Levels 7 β 11
There are 5 levels in this lab. Each level uses a different text processing command.
Note: The reason we use sort before using the uniq command in level 8 is because the -u flag only removes adjacent duplicates. This means that it has no memory of previous lines. Using sort will group together all the duplicates.
What you've learnt: A surprising amount of analysis can be done by piping the correct commands together.
Lab 4: Recursive Decompression
Level 12 β 13
This is the level everyone complains about, and it deserves its reputation. You're given a hexdump of a file that's been compressed through multiple layers β gzip, bzip2, tar, sometimes compress β in an order you don't know going in, with no hints about how many layers there are.
Work in a scratch directory so you're not trashing your home folder:
mkdir /tmp/mywork12 && cd /tmp/mywork12
cp ~/data.txt .
xxd -r data.txt > data.bin
file data.binmkdir /tmp/mywork12 && cd /tmp/mywork12
cp ~/data.txt .
xxd -r data.txt > data.bin
file data.binThen it's a loop: see what file says, extract accordingly (gzip -d, bzip2 -d, tar -xf), run file again, repeat. It typically takes five to eight rounds before you land on ASCII text.
There's no trick that shortcuts this. It's purely a patience test, and the sooner you accept that, the less frustrating it is.
What this lab actually teaches: that sometimes the correct approach really is just mechanical persistence.
Lab 5: SSH Keys & Network Sockets
Levels 13 β 17
chmod 600 sshkey.private
ssh -i sshkey.private bandit14@bandit.labs.overthewire.org -p 2220
cat /etc/bandit_pass/bandit14chmod 600 sshkey.private
ssh -i sshkey.private bandit14@bandit.labs.overthewire.org -p 2220
cat /etc/bandit_pass/bandit14From here on, the password for each level lives directly at /etc/bandit_pass/banditN once you're logged in β no more hunting.
Levels 14 and 15 want you to submit your password to a listening port β plain TCP first, then SSL-wrapped:
echo <bandit14_password> | nc localhost 30000
echo <bandit15_password> | openssl s_client -connect localhost:30001 -quiet 2>/dev/nullecho <bandit14_password> | nc localhost 30000
echo <bandit15_password> | openssl s_client -connect localhost:30001 -quiet 2>/dev/nullLevel 16 is the first time the game makes you do real reconnaissance instead of following instructions. Somewhere in ports 31000β32000 is a service that trades your password for an RSA key β but several ports in that range just echo noise or close the connection on you.
nmap -sV -p31000-32000 localhostnmap -sV -p31000-32000 localhostThen it's manual trial against each candidate with openssl s_client until one of them hands back a full -----BEGIN RSA PRIVATE KEY----- block.
Level 17 is a breather β two files, and the password is whichever line changed between them:
diff passwords.old passwords.newdiff passwords.old passwords.newWhat this lab actually teaches: that port scanning narrows your search, but confirming the right service still takes manual verification.
Lab 6: Bypassing a Hostile .bashrc
Level 18 β 19
This level is genuinely clever design. You SSH in, and the account's .bashrc logs you straight back out before you get a prompt. It's not broken β it's intentional, and fighting it with the shell is the wrong move. Instead, hand SSH a command to execute remotely, which runs before .bashrc gets the chance:
ssh bandit18@bandit.labs.overthewire.org -p 2220 "cat readme"ssh bandit18@bandit.labs.overthewire.org -p 2220 "cat readme"What this lab actually teaches: SSH can execute a one-off command without ever opening an interactive shell β a distinction that matters a lot in automation and in exactly these kinds of restricted environments.
Lab 7: SUID Binary Exploitation
Levels 19 β 20
SUID is one of those concepts that sounds abstract until you watch it work. A SUID binary runs with the permissions of whoever owns it, not whoever calls it. bandit20-do is owned by bandit20, so running it as bandit19 still executes as bandit20:
./bandit20-do cat /etc/bandit_pass/bandit20./bandit20-do cat /etc/bandit_pass/bandit20Level 20 complicates it: suconnect connects out to a port and checks whatever it receives against the current password β but you have to be the one listening, feeding it the password, while suconnect connects to you:
# one terminal
nc -lvp <port>
# another terminal
./suconnect <port># one terminal
nc -lvp <port>
# another terminal
./suconnect <port>What this lab actually teaches: misconfigured SUID binaries are one of the most common real-world Linux privilege-escalation paths β this isn't a toy concept, it shows up constantly in actual engagements.
Lab 8: Cron-Based Privilege Escalation
Levels 21 β 24
cat /etc/cron.d/*cat /etc/cron.d/*Levels 21 and 22 just want you to read what's scheduled and follow a readable script to wherever it writes its output.
Level 23 asks you to actually understand the script, not just read it β it computes its output path dynamically by hashing something (typically the username), and if you reproduce that hash yourself, you know the filename before the job even fires:
echo -n "I am user bandit24" | md5sumecho -n "I am user bandit24" | md5sumLevel 24 drops a daemon that wants a password plus a 4-digit PIN, and disconnects you on any wrong guess β so brute-forcing one attempt per connection is impractical. The fix is feeding every possible PIN through a single connection:
bash
for i in $(seq -w 0000 9999); do
echo "<bandit24_password> $i"
done > pins.txt
nc localhost <port> < pins.txt | grep -B2 Correctfor i in $(seq -w 0000 9999); do
echo "<bandit24_password> $i"
done > pins.txt
nc localhost <port> < pins.txt | grep -B2 CorrectWhat this lab actually teaches: a cron job running as a privileged user is a trust boundary β readable scripts and predictable paths are both real attack surfaces.
Lab 9: Restricted Shell Escapes
Levels 25 β 27
Level 25 logs you into something that isn't bash at all β it's more, standing in as your shell. You never get a real prompt. But more has its own command set, and its v command opens the current content in a text editor β which, once loaded, gives you a genuine shell underneath.
Level 26 layers a terminal-resize quirk on top of the same trick, and from the resulting shell, a SUID binary finishes the job:
./bandit27-do cat /etc/bandit_pass/bandit27./bandit27-do cat /etc/bandit_pass/bandit27What this lab actually teaches: a restricted shell only restricts the shell itself β any other interactive program it still lets you launch is a potential way out.
Lab 10: Git Internals
Levels 27 β 31
This stretch is my favorite in the whole game, because it quietly teaches something a lot of people never fully internalize about version control: deleting a file from the current state doesn't delete it from history.
git clone ssh://bandit27-git@bandit.labs.overthewire.org:2220/home/bandit27-git/repo
cat repo/README.mdgit clone ssh://bandit27-git@bandit.labs.overthewire.org:2220/home/bandit27-git/repo
cat repo/README.md**Level 28'**s README has been scrubbed β but it existed in an earlier commit, and git remembers:
git log
git show <commit-hash>git log
git show <commit-hash>Level 29 isn't in any commit on the current branch β check what else exists:
git branch -a
git checkout <branch-name>git branch -a
git checkout <branch-name>Level 30 isn't in a branch either β it's on a tag:
git tag
git show <tag-name>git tag
git show <tag-name>Level 31 flips the pattern: instead of reading something out, you push something in. Specifically, a file key.txt containing exactly May I come in?, pushed to master. The trap is the repo's .gitignore, which excludes all .txt files β your git add will silently do nothing unless you clear it first:
cat /dev/null > .gitignore
echo "May I come in?" > key.txt
git add -A
git commit -m "key"
git push origin mastercat /dev/null > .gitignore
echo "May I come in?" > key.txt
git add -A
git commit -m "key"
git push origin masterThe push gets rejected on purpose β but the password is sitting in the rejection output before the error ever prints.
What this lab actually teaches: git history, branches, and tags are all independently recoverable β "deleting" a file from the working tree is nowhere near the same as removing it from the repository.
Lab 11: Shell Variable Exploitation
Level 32 β 33 (final)
The last level drops you into "uppershell," which force-uppercases every command before running it. ls becomes LS, cat becomes CAT, everything breaks.
The way out is a shell-internal variable, not a tool: $0 holds the path of the currently running shell, and referencing it directly spawns a fresh instance β untouched by the uppercase wrapper:
$0
cat /etc/bandit_pass/bandit33$0
cat /etc/bandit_pass/bandit33Thing I Learn : Sometimes escaping is as simple as knowing a single obscure fact about shells.
Some key takeaways from this lab:
- SUID permission inheritance was used three times. This is not an edge case and will be a common theme when working on real privilege escalation scenarios.
- Cron jobs were exploited by running as a privileged user. This was exploited from three different angles.
- Git doesn't forget. Assuming the repo is in its current state is a mistake that can cause issues during real incidents.
- Restricted shells only restrict the shell itself. Once this was realised, escaping the restricted shell became less of a mystery and more of a question of what else can this shell open.
If you found this useful I have also written a story based TryHackMe walkthrough. I have written Break Out The Cage which is a Nicolas Cage themed box covering anonymous FTP, Vigenère cipher and cron command injection. You can find it here. Make sure to follow for more practical Linux and security writeups.