OverTheWire: Bandit — Levels 7-15¶
Reference: https://overthewire.org/wargames/bandit/
See also:
level_1-5.md.mdfor levels 0-6, SSH basics,findreference, hidden files, special filenames.
Level 7 → 8¶
Password: morbNTDkSW6jIlUc0ymOdMaLnOlFVAaj
Concept: Find the password next to the word "millionth" in data.txt.
grep = search for a pattern in a file. Returns the entire line containing the match.
data.txt has thousands of lines in word: password format. Rather than reading through it, you search for the specific word you know is next to the password.
Level 8 → 9¶
Password: dfwvzFQi4mU0wfNbFOe9RoWskMLg7eEc
Concept: The password is the only line that appears exactly once in data.txt.
Full breakdown:
| Part | What it does |
|---|---|
sort data.txt |
Sort all lines alphabetically. uniq only works on adjacent duplicate lines — sort puts duplicates next to each other |
\| |
Pipe sorted output into uniq |
uniq -u |
Print only lines that appear exactly once (-u = unique). Lines appearing 2+ times are suppressed |
Why sort first? uniq compares adjacent lines only. Without sorting, duplicate lines scattered throughout the file wouldn't be detected. Sorting groups identical lines together so uniq can find them.
uniq flag reference:
| Flag | What it does |
|---|---|
-u |
Only print unique lines (appear exactly once) |
-d |
Only print duplicate lines (appear more than once) |
-c |
Prefix each line with its count |
| (none) | Remove consecutive duplicates, print one of each |
The mistake in the raw notes was using uniq without -u and trying to count manually — -u does exactly that.
Level 9 → 10¶
Password: 4CKMh1JI91bUIZZPXDqGanal4xvAg0JM
Concept: Password is in data.txt (a binary file) as one of the few human-readable strings, preceded by = characters.
| Part | What it does |
|---|---|
strings data.txt |
Extract all sequences of printable characters (4+ chars by default) from any file — ignores binary bytes |
grep '==' |
Filter for lines containing == (the = characters before the password) |
file vs strings:
- file = tells you the type of a whole file
- strings = pulls readable text out of a file regardless of its type
Useful whenever a binary/compiled file might contain embedded plaintext (passwords, config, keys).
Level 10 → 11¶
Password: FGUW5ilLVJrxX9kMYMmlN4MgbpfMiqey
Concept: data.txt contains base64-encoded data.
base64 -d = decode base64. -d flag means decode (same as --decode).
What is base64? A way to encode binary data as printable ASCII characters. Uses characters A-Z, a-z, 0-9, +, /, and = as padding. You'll recognise it by the alphanumeric string often ending with = or ==.
It's encoding, not encryption — anyone can decode it instantly. Used for data transport (email attachments, JWTs, Kubernetes secrets) not for security.
Level 11 → 12¶
Password: dtR173fZKb0RRsDFSGsg2RWnpNVj3qRr
Concept: data.txt has been ROT13 encoded.
What is ROT13?
Rotate by 13 — every letter shifts 13 positions forward in the alphabet:
A B C D E F G H I J K L M → N O P Q R S T U V W X Y Z
N O P Q R S T U V W X Y Z → A B C D E F G H I J K L M
A→N, B→O, M→Z, N→A. The alphabet has 26 letters, 13 is exactly half — so applying ROT13 twice gets you back to the original. Encrypt and decrypt are the same operation.
Zero security — just shifts letters. Used in CTFs as light obfuscation.
tr = translate characters:
Maps each character from set1 to the character at the same position in set2, one-to-one.
echo "hello" | tr 'a-z' 'A-Z' # HELLO — lowercase to uppercase
echo "hello" | tr -d 'aeiou' # hll — delete vowels (-d = delete)
echo "a b" | tr -s ' ' # a b — squeeze multiple spaces into one (-s = squeeze)
echo "hello" | tr 'a-z' 'n-za-m' # uryyb — ROT13 lowercase only
ROT13 mapping breakdown:
- A-Z (26 chars) maps to N-ZA-M = N through Z (13 chars) + A through M (13 chars)
- a-z maps to n-za-m same logic for lowercase
- Non-letter characters pass through unchanged
tr only reads from stdin — you can't give it a filename directly. Always pipe into it.
Level 12 → 13¶
Password: 7x16WNeHIi5YkIhWsfFIqoognUTyj9Q4
Concept: data.txt is a hexdump of a file that has been repeatedly compressed.
This level is a chain — you reverse the hexdump, then decompress multiple times, each time checking the file type to know which decompression tool to use.
# 1. Create a working directory (can't write to home)
mkdir /tmp/work && cd /tmp/work
cp ~/data.txt .
# 2. Reverse the hexdump back to binary
xxd -r data.txt > data.bin
# 3. Check what type it is
file data.bin
# 4. Rename and decompress — repeat until you get ASCII text
mv data.bin data.gz && gzip -d data.gz # if gzip
mv data.bin data.bz2 && bzip2 -d data.bz2 # if bzip2
mv data.bin data.tar && tar -xf data.tar # if tar
# Keep running: file <result> → rename → decompress → repeat
# Until: file <result> shows "ASCII text"
Tools used:
| Tool | What it does | Command |
|---|---|---|
xxd |
Hexdump utility — xxd file = to hex, xxd -r = from hex back to binary |
xxd -r data.txt > data.bin |
file |
Identify file type by magic bytes | file data.bin |
gzip |
GNU zip compression | gzip -d file.gz (decompress) |
bzip2 |
bzip2 compression (better ratio than gzip) | bzip2 -d file.bz2 |
tar |
Tape archive — bundles multiple files, optionally compressed | tar -xf file.tar |
tar flags:
| Flag | What it does |
|---|---|
-x |
Extract |
-f |
Next argument is the filename |
-z |
gzip compressed (.tar.gz / .tgz) |
-j |
bzip2 compressed (.tar.bz2) |
-v |
Verbose — print files as extracted |
-c |
Create archive |
Why can't you just decompress without renaming? Tools like gzip check the file extension. gzip -d file without .gz extension errors out. file tells you the actual type, then you rename to match.
Level 13 → 14¶
Password: FO5dwFsc0cbaIiH0h8J2eUks2vdTDwAn
Concept: No password for this level — you get an SSH private key file (sshkey.private) that logs you directly into bandit14.
-i = identity file (private key). Instead of a password, SSH uses the key pair for authentication.
Level 14 → 15¶
Password: 8xCjnmgoKbGLhHFAZlGE5Tmu4M2tKJQo
Concept: You logged in with a private key so you never typed the password. But you need it to submit to port 30000. It's stored at /etc/bandit_pass/bandit14 (readable only by bandit14, which you are).
# 1. Get your current level's password
cat /etc/bandit_pass/bandit14
# 2. Submit it to port 30000 on localhost
echo "MU4VWeTyX8kuzw8s7Xf9OHxq4Uqzsr1J" | nc localhost 30000
nc = netcat — the Swiss Army knife of networking.
Opens a raw TCP (or UDP) connection to a host:port. Whatever you pipe in gets sent. Whatever the remote end responds with gets printed.
nc <host> <port> # interactive connection
echo "data" | nc host port # send data and print response
nc -l -p 1234 # listen on port 1234 (simple server)
Here: you're connecting to localhost:30000 where a service is running. It expects the current level's password — you send it, it replies with the next level's password.
/etc/bandit_pass/ — where all passwords live:
Every level's password is at /etc/bandit_pass/banditN. Each file is only readable by that user. As bandit14 you can read /etc/bandit_pass/bandit14 but not bandit15.
How to find this yourself if you didn't know:
Pattern recognition — /etc is always worth enumerating. Credentials, configs, and system-wide files live there by convention.
Check what's listening on a port:
ss -tlnp | grep 30000 # ss = socket statistics, modern replacement for netstat
nmap -p 30000 localhost # nmap port scan
ss flag |
What it does |
|---|---|
-t |
TCP only |
-l |
Listening sockets only |
-n |
Show numbers not names (port 80 not http) |
-p |
Show process name/PID |
Level 15 → 16¶
Concept: Same as level 14→15 but port 30001, using SSL/TLS.
Then type the password and press Enter. openssl s_client opens an SSL-encrypted connection — the raw TCP of nc won't work here because the service requires TLS.
Or pipe it:
-quiet suppresses the verbose SSL handshake output.
Command Reference — New Tools This Set of Levels¶
# Text searching
grep "pattern" file # lines matching pattern
grep -n "pattern" file # with line numbers
grep -i "pattern" file # case insensitive
grep -v "pattern" file # lines NOT matching
# Sorting & deduplication
sort file # sort alphabetically
sort -n file # sort numerically
sort -r file # reverse sort
uniq file # remove consecutive duplicates
uniq -u file # unique lines only (appear once)
uniq -d file # duplicate lines only
uniq -c file # count occurrences
sort file | uniq -u # find line appearing exactly once
# Binary file inspection
strings file # extract readable text from binary
xxd file # hexdump
xxd -r hexfile > binary # reverse hexdump
# Encoding
base64 -d file # decode base64
echo -n "text" | base64 # encode to base64
# Character translation
tr 'a-z' 'A-Z' # lowercase to uppercase
tr -d 'chars' # delete characters
tr -s ' ' # squeeze repeated spaces
cat file | tr 'A-Za-z' 'N-ZA-Mn-za-m' # ROT13
# Compression
gzip -d file.gz # decompress gzip
bzip2 -d file.bz2 # decompress bzip2
tar -xf file.tar # extract tar
tar -xzf file.tar.gz # extract tar+gzip
tar -xjf file.tar.bz2 # extract tar+bzip2
# Networking
nc host port # raw TCP connection
echo "data" | nc host port # send data, print response
ss -tlnp # list listening ports
nmap -p PORT host # scan specific port
openssl s_client -connect host:port # SSL/TLS connection
# SSH
ssh -p 2220 user@host # custom port
ssh -i keyfile -p 2220 user@host # private key auth
chmod 400 keyfile # private keys must be owner-read-only or SSH refuses
Lessons from These Levels¶
man <command> is worth 60 seconds. uniq -u would've been found immediately. After finishing a level, skim the man page for the tool you used — flags like -u, -d, -c stick once you have context.
file before anything else when dealing with mystery binary files. Don't guess the compression format — check. file data → rename → decompress → repeat.
/etc is always worth enumerating on any Linux box. Credentials, configs, system-wide settings all live there by convention.
2>/dev/null whenever searching from / — otherwise permission denied errors from /proc, /sys, /root bury the actual results.