OverTheWire Bandit walkthrough

Published: April 23, 2025, updated: April 23, 2025

This article contains a walkthrough for all 34 levels of the Bandit wargame on OverTheWire.

The OverTheWire wargames have been on the web for a long time. You don’t have to pay anything to play them. The OverTheWire maintainers are volunteering a lot of time to teach everyone about Linux security. Please consider donating to OverTheWire.

I’ve solved most of the levels in Bandit around 2017. I thought it would be useful to write up the shell commands I’ve used to solve each level without giving away the solution password themselves. Use this guide if you feel stuck and try your best coming up with your own original shell invocations.

I’ve marked all passwords in the command outputs with XXX... or YYY....

And most importantly, have fun :)

Bandit level 0

Link to this level: https://overthewire.org/wargames/bandit/bandit0.html

To solve level 0, you need to log in to bandit0 with password bandit0 using SSH on bandit.labels.overthewrite.org and port 2220.

Connect with ssh by using the following command:

ssh bandit0@bandit.labs.overthewire.org -p 2220

When you connect successfully, you should see a message like this:

                         _                     _ _ _
                        | |__   __ _ _ __   __| (_) |_
                        | '_ \ / _` | '_ \ / _` | | __|
                        | |_) | (_| | | | | (_| | | |_
                        |_.__/ \__,_|_| |_|\__,_|_|\__|


                      This is an OverTheWire game server.
            More information on http://www.overthewire.org/wargames

...

  Enjoy your stay!

bandit0@bandit:~$

Bandit level 1

Link to this level: https://overthewire.org/wargames/bandit/bandit1.html

Connect to this level’s account using the following command:

# The password is the same as level 0
ssh bandit0@bandit.labs.overthewire.org -p 2220

Read out the file called readme using the cat command and learn the password for the next level:

cat readme

This prints out the following:

Congratulations on your first steps into the bandit game!!
Please make sure you have read the rules at https://overthewire.org/rules/
If you are following a course, workshop, walkthrough or other educational activity,
please inform the instructor about the rules as well and encourage them to
contribute to the OverTheWire community so we can keep these games free!

The password you are looking for is: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Note the password contained within the readme file and proceed to the next level’s account on Bandit.

Bandit level 2

Link to this level: https://overthewire.org/wargames/bandit/bandit2.html

Use ssh to connect to the bandit1 account on Bandit:

# Use the password from the previous level here:
ssh bandit1@bandit.labs.overthewire.org -p 2220

This level stores its password in a file called -. Trying to just read out the file using the command cat - doesn’t work. The program cat assumes that you are passing it something over standard input.

Refer to the manual of cat, retrieved on Bandit using man cat. Here you can see that cat interprets the file called - in a specific way:

NAME cat - concatenate files and print on the standard output

SYNOPSIS cat [OPTION]… [FILE]…

DESCRIPTION Concatenate FILE(s) to standard output.

With no FILE, or when FILE is -, read standard input.

The solution is to prepend the filename - with a ./. The following command reveals the password for the next level:

cat ./-

Bandit level 3

Link to this level: https://overthewire.org/wargames/bandit/bandit3.html

Connect to bandit using the following command:

# Enter password from level 1
ssh bandit2@bandit.labs.overthewire.org -p 2220

Solve the next level by escaping all spaces in the target filename. In bash, put a backward slash \ before any spaces in a filename.

Here’s how to escape spaces in the filename spaces in this filename and print out its contents:

cat spaces\ in\ this\ filename
# You can also type this:
cat "spaces in this filename"
# or this
cat 'spaces in this filename'

Note the password and use it in the next level.

Bandit level 4

Link to this level: https://overthewire.org/wargames/bandit/bandit4.html

This level has a hidden filename. In many UNIX operating systems, filenames are “hidden” if they start with a period . character.

Connect to the bandit3 account using the following command:

# Enter password from level 3
ssh bandit3@bandit.labs.overthewire.org -p 2220

Then run the following command to show the password for the next level:

cat inhere/.hidden

Note the password and use it in the next level.

Bandit level 5

Link to this level: https://overthewire.org/wargames/bandit/bandit5.html

This level has a bunch of files in the inhere directory. Only one of them is human-readable and contains the password. Your task is to find this file.

Connect to the level using ssh:

# Use password from level 3 to log in
ssh bandit4@bandit.labs.overthewire.org -p 2220

Find out what files are in the inhere directory:

bandit4@bandit:~$ ls inhere
-file00  -file01  -file02  -file03  -file04  -file05  -file06  -file07  -file08  -file09

Using the head command, you can print the beginning of each file and the name of that file. Try running head inhere/* and find the file that has the password for the next level:

bandit4@bandit:~$ head inhere/*
==> inhere/-file00 <==
QRrtZi  H
         |ȧ^
==> inhere/-file01 <==
7L3Yͯ    ŴEY     V&hF
==> inhere/-file02 <==
O̫`\-⃐Hx2K
==> inhere/-file03 <==
ix#e>VOp{       MUb4
==> inhere/-file04 <==
gQeE}:gj8<.e
==> inhere/-file05 <==
S 0]7b<~
==> inhere/-file06 <==
G=1B׃"
      W9ؽ5
==> inhere/-file07 <==
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

==> inhere/-file08 <==
K~+9"T*Z$"r

==> inhere/-file09 <==

In my case, inhere/-file07 was the winner. Note the password and use it in the next level.

Bandit level 6

Link to this level: https://overthewire.org/wargames/bandit/bandit6.html

This level has you look for a specific file within the inhere directory. The file isn’t executable, 1033 bytes long, and human-readable. The find command is useful for finding a file using specific criteria like these.

First, connect to this level using ssh:

# Use the password from level 5
ssh bandit5@bandit.labs.overthewire.org -p 2220

This is the find invocation that found the file for me.

find inhere -type f -size 1033c -exec cat {} \;

The flags used here do the following:

Find may print out a bunch of gibberish files as well. Note the password and use it in the next level.

Bandit level 7

Link to this level: https://overthewire.org/wargames/bandit/bandit7.html

This level is a continuation of the previous level. Use find again to solve it. This time, the winning file belongs to the user bandit7, is owned by the group bandit6, and is 33 bytes long. The file could be anywhere on the system and not only in the bandit6 user’s home directory.

First, connect to the level using ssh:

ssh bandit6@bandit.labs.overthewire.org -p 2220

Use the following find invocation to find the file:

find / -size 33c \
  -user bandit7 \
  -group bandit6 -exec head {} \; 2> /dev/null

The arguments used for the find command used this time do the following:

Discarding errors is important here because the find command produces a lot of errors when scanning through the complete filesystem. In the machines filesystems there are a lot of files that the bandit6 user isn’t allowed to access. The find command informs you of every error that it encounters when reading files. This can cause it to produce a lot of output and makes finding the file you are looking for difficult.

Bandit level 8

Link to this level: https://overthewire.org/wargames/bandit/bandit8.html

In this level, you need to search the file data.txt for the next level’s password. The password is written right next to the string millionth. Use the grep command to solve this level.

Connect to the level using ssh:

ssh bandit7@bandit.labs.overthewire.org -p 2220

The following regular expression used with grep finds the word millionth and anything written after it:

millionth.*

Here’s how to use it with the grep command:

grep -e 'millionth.*' data.txt

You should see something like the following:

millionth       XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Note the password written after millionth and use it for the next level.

Bandit level 9

Link to this level: https://overthewire.org/wargames/bandit/bandit9.html

In this level, you need to find a line that occurs only once in a file called data.txt. Every other line in this file appears at least twice.

Connect using ssh:

ssh bandit8@bandit.labs.overthewire.org -p 2220

Use sort piped into uniq --unique to first sort the lines, exclude duplicate lines, and then only output the lines that appear exactly once.

sort < data.txt | uniq --unique

This prints the password for the next level.

Bandit level 10

Link to this level: https://overthewire.org/wargames/bandit/bandit10.html

This level wants you to use grep to search for the password for the next level. The password is one of the only human-readable strings and is preceded by at least one = character.

Connect using ssh:

ssh bandit9@bandit.labs.overthewire.org -p 2220

Use the strings command to extract human-readable strings from a file. Pipe the result into grep and only output lines that start with at least two = characters. These two commands put together look like this:

strings data.txt | grep -e '^=='

The output for this invocation looks like this:

========== passwordk^
========== is
========== XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Note down the password and use it in Bandit level 11.

Bandit level 11

Link to this level: https://overthewire.org/wargames/bandit/bandit11.html

This level gives you the password in a file called data.txt. This file contains Base64 encoded data that you have to decode to get the password.

Connect using ssh:

ssh bandit10@bandit.labs.overthewire.org -p 2220

Run the following command and observe the output:

base64 -d data.txt

You should see something like the following:

The password is XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Bandit level 12

Link to this level: https://overthewire.org/wargames/bandit/bandit12.html

In this level, the file data.txt contains a ROT13 encoded password. To continue to the next level, you have to find a way to decode this password.

Connect using ssh:

ssh bandit11@bandit.labs.overthewire.org -p 2220

If you scroll down the Wikipedia article linked here, you can find the solution for decoding ROT13 with tr here.

Run the following tr invocation to find the next level’s password:

tr 'A-Za-z' 'N-ZA-Mn-za-m' < data.txt

You should see the following output after running this command:

The password is JVNBBFSmZwKKOP0XbFXOoW8chDz5yVRv

Bandit level 13

Link to this level: https://overthewire.org/wargames/bandit/bandit13.html

This level has you unpack, decode, or decompress a file 8 times to find the password. The order of operation from start to finish is:

  1. Compress with gzip
  2. Archive with tar
  3. Archive with tar a second time
  4. Archive with tar a third time
  5. Compress with gzip
  6. Compress with bzip2
  7. Compress with gzip
  8. Convert to hex dump with xxd

When unpacking this file, you need to run these operations in reverse.

Connect using ssh:

ssh bandit12@bandit.labs.overthewire.org -p 2220

To convert the hex dump back into binary data, use xxd -r:

xxd -r < data.txt

This outputs binary data to your terminal and won’t immediately be useful. To see what format this binary output is, pipe the output of xxd -r into file - like so:

xxd -r < data.txt | \
  file -

This should print that it’s gzip compressed data:

/dev/stdin: gzip compressed data, was "data2.bin"
last modified: Thu Oct  5 06:19:20 2023, max compression, from Unix

Decompress this data using zcat and check the output format like so:

xxd -r < data.txt | \
  zcat | \
  file -

This time file tells you that the data is bzip2 compressed:

/dev/stdin: bzip2 compressed data, block size = 900k

Decompress this data using bzip2 --decompress --force and output it to standard out with the --stdout flag. This way, you can pipe the output into the next program. Inspect the contents again using file -:

xxd -r < data.txt | zcat | \
  bzip2 --decompress --force --stdout | file -

file tells you that the output is a tar archive:

/dev/stdin: POSIX tar archive (GNU)

Now, to unpack the contents, decompress the contents into a new temporary directory:

# change into temporary directory
cd $(mktemp -d)
# Will CD into something like this:
# /var/folders/k8/jnlz0xdn5jd48zttf3ktv7200000gn/T/tmp.0eOTs8AHV5
xxd -r < $HOME/data.txt | \
  zcat | \
  bzip2 --decompress --force --stdout | \
  zcat | \
  tar -xf -

Now check the contents of each unpacked file using file:

# Still in the same temporary directory as above
file *

file reports that there is one file data5.bin containing another tar archive:

data5.bin: POSIX tar archive (GNU)

Unpack the archive using the following command:

# Unpack data5.bin into the current directory
tar xvf data5.bin

This unpacks a file called data6.bin. This file is another tar archive. Here’s how you can unpack data6.bin:

# Unpack data6.bin into the same directory
tar xvf data6.bin

The file data6.bin contains a gzip compressed file called data8.bin.

In total, you should now have three uncompressed files:

# Contents of current temporary directory
data5.bin  data6.bin  data8.bin

Here’s what file reports when you run it on data8.bin:

data8.bin: gzip compressed data, was "data9.bin", last modified: Thu Oct  5 06:19:20 2023, max compression, from Unix, original size modulo 2^32 49

Print the contents of data8.bin by using zcat:

zcat data8.bin

Note the password contained in the file and use it in the next level:

The password is XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Bandit level 14

Link to this level: https://overthewire.org/wargames/bandit/bandit14.html

This level gives you an SSH key that you can use to log into Bandit level 14. Connect to level 13 using SSH:

ssh bandit13@bandit.labs.overthewire.org -p 2220

After you connect, print out the SSH private key contained in sshkey.private and store it on your own machine:

cat sshkey.private

The SSH private key looks like this:

-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAxkkOE83W2cOT7IWhFc9aPaaQmQDdgzuXCv+ppZHa++buSkN+
... rest left out
/+aLoRQ0yBDRbdXMsZN/jvY44eM+xRLdRVyMmdPtP8belRi2E2aEzA==
-----END RSA PRIVATE KEY-----

Store the whole private key in a file called level_14.key and use it in the next level.

Bandit level 15

Link to this level: https://overthewire.org/wargames/bandit/bandit15.html

In this level, you need to send the current level’s password to a TCP server listening on port 30000 on the same machine. Where’s the current level’s password if the only thing you have is the SSH key? Passwords for each level are stored in /etc/bandit_pass. Each password file for a level is only accessible by that levels’ account. For example, the user bandit14 can access the file /etc/bandit_pass/bandit14.

Connect to the level using ssh and give it the private key file level_14.key from the last level.

ssh bandit14@bandit.labs.overthewire.org -p 2220 -i level_14.key

If SSH complains about the key at level_14.key being world readable, run the following command to fix its permissions:

chmod 400 level_14.key

After you’ve connected, read out the password for bandit14 stored in /etc/bandit_pass/bandit14.

# Read out the password and note it
cat /etc/bandit_pass/bandit14

Use nc to pass the password to port 30000 on the same machine:

#     Contents of `/etc/bandit_pass/bandit14`
#     v
echo "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" | nc localhost 30000

When you run this command, the server on port 30000 gives you the password for the next level. Write it down and use it for the next level.

Bandit level 16

Link to this level: https://overthewire.org/wargames/bandit/bandit16.html

To solve level 16, you need to forward the current level’s password to a server listening on the same machine. The difference to level 15 is that this time you need to connect to it using TLS encryption.

Connect to the level using ssh:

ssh bandit15@bandit.labs.overthewire.org -p 2220 -i /dev/null

The command that you can use to connect to a server using TLS encryption is called openssl s_client. OpenSSL is full of useful tools for certificate related operations and features a TLS client called s_client as well.

Here’s the command that solves this level:

#     Password from level 15
#     v
echo "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" | \
  openssl s_client -quiet -connect localhost:30001

When you pass the -quiet flag to openssl s_client, it won’t print connection status or certificate information. This information is useful for debugging, but can be noisy when you just want to see the flag.

After you run the preceding command, you should receive the password for the next level.

Bandit level 17

Link to this level: https://overthewire.org/wargames/bandit/bandit17.html

To solve this level, you need to do 3 things:

  1. Find which port on the current machine between 31000 and 32000 is open.
  2. Find out which of these open ports lets you connect with TLS.
  3. Connect to the right port and send the current level’s password and get the next level’s password.

Connect to the current level using ssh:

ssh bandit16@bandit.labs.overthewire.org -p 2220 -i /dev/null

I admit that I’ve brute-forced this level. I tried all 1000 ports and directly connected with openssl s_client. This script discards all error messages to /dev/null. The output is long and verbose. If you scroll through the output, you should eventually see the private key for the next level. Here’s the script that you can just paste and run in the SSH session:

for i in $(seq 31000 32000); do
    echo "Trying port $i"
    #    Current level's password
    #     v
    echo "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" | \
      openssl s_client -quiet -connect "localhost:$i" 2>/dev/null
done

The OpenSSL invocation is the same as in the previous level and uses -quiet again. This script pipes any error messages when OpenSSL fails to connect to /dev/null and thus discards them. The script runs for a while and at some point you should see the following output:

[...]
Correct!
-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAvmOkuifmMg6HL2YPIOjon6iWfbp7c3jx34YkYWqUH57SUdyJ
...
vBgsyi/sN3RqRBcGU40fOoZyfAMT8s1m/uYv52O6IgeuZ/ujbjY=
-----END RSA PRIVATE KEY-----
[...]

Save this private key in a file called level_17.key and move on to the next level.

Bandit level 18

Link to this level: https://overthewire.org/wargames/bandit/bandit18.html

In this level, you need to spot the difference between two files passwords.old and passwords.new. Only one line has changed, and the changed line in passwords.new is the password for the next level.

Connect to bandit17 using ssh with the private key level_17.key from the last level.

# Fix key permissions if necessary
chmod 400 level_17.key
ssh bandit17@bandit.labs.overthewire.org -p 2220 -i level_17.key

Use the diff command to print out the differences between passwords.old and passwords.new like so:

diff -u passwords.old passwords.new

You should see the following output. The line marked with a + character in the beginning tells you what the new line in passwords.new is. The line with the - character is the line in passwords.old that someone has deleted.

--- passwords.old       2024-07-17 15:57:13.545795226 +0000
+++ passwords.new       2024-07-17 15:57:13.549795225 +0000
@@ -XX,7 +XX,7 @@
 OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
 OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
 OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
-OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
+NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN
 OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
 OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
 OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO

The password is obfuscated in this output. The line marked with the + character is the password for the next level.

Bandit level 19

Link to this level: https://overthewire.org/wargames/bandit/bandit19.html

Messing with SSH to escape user shell configurations is a real method that penetration testers use.

A recent vulnerability report, CVE-2024-47516 in Fedora Pagure shows this. Read the full report here

The authors identify weaknesses that when combined allow users to run arbitrary code when connecting to git@gitinstance.com with an SSH client. This is a no-privilege, low-complexity, and network exploitable vulnerability with high CIA impact.

In this level, you need to leverage a similar vulnerability and read out the readme file in user bandit18’s home directory. Directly connecting with ssh doesn’t work. The .bashrc is set to automatically kick you out.

To read out the readme file, pass the command that you want to run directly to SSH. This is how to do it:

ssh bandit18@bandit.labs.overthewire.org -p 2220 \
  cat readme

This is what you should see when it runs successfully, where XXX... is the password for the next level:

                         _                     _ _ _
                        | |__   __ _ _ __   __| (_) |_
                        | '_ \ / _` | '_ \ / _` | | __|
                        | |_) | (_| | | | | (_| | | |_
                        |_.__/ \__,_|_| |_|\__,_|_|\__|


                      This is an OverTheWire game server.
            More information on http://www.overthewire.org/wargames

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Bandit level 20

Link to this level: https://overthewire.org/wargames/bandit/bandit20.html

In this level you need to use a setuid command to gain another user’s privileges. The challenge server stores Bandit level password in the /etc/bandit_pass/ directory. Each Bandit level user can only access their own password in there. For this level, you connect as bandit19 and you can only read the password in /etc/bandit_pass/bandit19. To read out the password for bandit20, you need to be able to become bandit20, at least temporarily. The bandit19 user’s home directory contains a setuid binary that can run commands as the user bandit20. The goal is to use this binary and read out the user bandit20’s password in the /etc/bandit_pass/ directory.

Connect to bandit19 using ssh:

ssh bandit19@bandit.labs.overthewire.org -p 2220

Here’s the setuid binary that lets you become bandit20:

./bandit20-do

Run without any arguments, bandit-20 prints the following:

Run a command as another user.
  Example: ./bandit20-do id

This is how you can read out the user bandit20’s password file:

./bandit20-do cat /etc/bandit_pass/bandit20

Note the password that it prints out and use it in the next level.

Bandit level 21

Link to this level: https://overthewire.org/wargames/bandit/bandit21.html

In this level, you need to send your password to a specific port using a setuid binary.

Connect to this level:

ssh bandit20@bandit.labs.overthewire.org -p 2220
nc -l 20000 # ctrl-z
./suconnect 20000 # ctrl-z
fg %1
# type password from the last level

Here’s how it should look like, with comments denoted with a # symbol:

# Start nc and listen on port 20000
bandit20@bandit:~$ nc -l 20000
# Put nc -l in the background by pressing ctrl-z
^Z
[1]+  Stopped                 nc -l 20000
# Start ./suconnect and connect to port 20000
bandit20@bandit:~$ ./suconnect 20000
# Put ./suconnect 20000 in the background by pressing ctrl-z
^Z
[2]+  Stopped                 ./suconnect 20000
# Put nc -l 20000 back in the foreground
bandit20@bandit:~$ fg %1
nc -l 20000
# Type the password from the last level
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
# Put nc -l 20000 in the background
^Z
[1]+  Stopped                 nc -l 20000
# Put ./suconnect 20000 in the foreground
bandit20@bandit:~$ fg %2
./suconnect 20000
# It shows you that it read the password correctly
Read: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
# The ./suconnect 20000 sends the password to nc -l 20000 and quits
Password matches, sending next password
# Put nc -l 20000 in the foreground
bandit20@bandit:~$ fg %1
nc -l 20000
# This is the password for the next level
YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY

Note the password that the nc -l receives and use it in the next level.

Bandit level 22

Link to this level: https://overthewire.org/wargames/bandit/bandit22.html

In this level, you need to understand what a specific cron job is doing to read out the password. Connect with SSH:

ssh bandit21@bandit.labs.overthewire.org -p 2220

Find the cron job description in /etc/cron.d and understand where it writes the password for the next level:

# List all cron job configurations
bandit21@bandit:~$ ls /etc/cron.d/
cronjob_bandit22  cronjob_bandit23  cronjob_bandit24  e2scrub_all  otw-tmp-dir  sysstat

# Read out the configuration for bandit22
bandit21@bandit:~$ cat /etc/cron.d/cronjob_bandit22
@reboot bandit22 /usr/bin/cronjob_bandit22.sh &> /dev/null
* * * * * bandit22 /usr/bin/cronjob_bandit22.sh &> /dev/null

# List the bandit22 user's cron job info at /usr/bin/cronjob_bandit22.sh
bandit21@bandit:~$ ls -l /usr/bin/cronjob_bandit22.sh
-rwxr-x--- 1 bandit22 bandit21 130 Jul 17 15:57 /usr/bin/cronjob_bandit22.sh

# Read out the cronjob file at /usr/bin/cronjob_bandit22.sh
bandit21@bandit:~$ cat /usr/bin/cronjob_bandit22.sh
#!/bin/bash
chmod 644 /tmp/YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY
cat /etc/bandit_pass/bandit22 > /tmp/YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY

# Read out the password written to the temporary file:
bandit21@bandit:~$ cat /tmp/YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Bandit level 23

Link to this level: https://overthewire.org/wargames/bandit/bandit23.html

This level is almost like the last level. The shell script that the cron job invokes is a bit more involved. Once you figure out how it determines the filenames, you can read out the password.

Connect to this level with SSH:

ssh bandit22@bandit.labs.overthewire.org -p 2220

Here are the steps I took to find the password:

# List all cron configurations
bandit22@bandit:~$ ls /etc/cron.d
cronjob_bandit22  cronjob_bandit23  cronjob_bandit24  e2scrub_all  otw-tmp-dir  sysstat

# Read out the cron configuration for bandit23
bandit22@bandit:~$ cat /etc/cron.d/cronjob_bandit23
@reboot bandit23 /usr/bin/cronjob_bandit23.sh  &> /dev/null
* * * * * bandit23 /usr/bin/cronjob_bandit23.sh  &> /dev/null

# Read out the cron job's script:
bandit22@bandit:~$ cat /usr/bin/cronjob_bandit23.sh
#!/bin/bash

myname=$(whoami)
mytarget=$(echo I am user $myname | md5sum | cut -d ' ' -f 1)

echo "Copying passwordfile /etc/bandit_pass/$myname to /tmp/$mytarget"

cat /etc/bandit_pass/$myname > /tmp/$mytarget

# Find the hash $mytarget used for the filename
bandit22@bandit:~$ echo I am user bandit23 | md5sum | cut -d ' ' -f 1
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

# Read out the file contents
bandit22@bandit:~$ cat /tmp/$(echo I am user bandit23 | md5sum | cut -d ' ' -f 1)
ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ

Bandit level 24

Link to this level: https://overthewire.org/wargames/bandit/bandit24.html

In this level, you have to trick a cron job into executing your script instead.

Connect to the level using SSH:

ssh bandit23@bandit.labs.overthewire.org -p 2220

There’s a cron job in /usr/bin/cronjob_bandit24.sh. You can trick it into running your script by putting an executable file in the right place:

bandit23@bandit:~$ cat /etc/cron.d/
cronjob_bandit22  cronjob_bandit24  otw-tmp-dir       sysstat
cronjob_bandit23  e2scrub_all       .placeholder
bandit23@bandit:~$ cat /etc/cron.d/cronjob_bandit24
@reboot bandit24 /usr/bin/cronjob_bandit24.sh &> /dev/null
* * * * * bandit24 /usr/bin/cronjob_bandit24.sh &> /dev/null
bandit23@bandit:~$ cat /usr/bin/cronjob_bandit24.sh
#!/bin/bash

myname=$(whoami)

cd /var/spool/$myname/foo
echo "Executing and deleting all scripts in /var/spool/$myname/foo:"
for i in * .*;
do
    if [ "$i" != "." -a "$i" != ".." ];
    then
        echo "Handling $i"
        owner="$(stat --format "%U" ./$i)"
        if [ "${owner}" = "bandit23" ]; then
            timeout -s 9 60 ./$i
        fi
        rm -f ./$i
    fi
done

bandit23@bandit:~$ ls /var/spool/bandit24

Here’s the command I used to write an executable shell script into /var/spool/bandit24. The shell script uses the install command to copy over the password file:

cat > /var/spool/bandit24/foo/cat_passwd <<EOF
#!/bin/bash
install --mode=444 /etc/bandit_pass/bandit24 /tmp/bandit_pass_bandit24
EOF
chmod +x /var/spool/bandit24/foo/cat_passwd
sleep 60
cat /tmp/bandit_pass_bandit24

Finally, this is how you can then print out the password:

cat /tmp/bandit_pass_bandit24

Note the password and use it in the next level.

Bandit level 25

Link to this level: https://overthewire.org/wargames/bandit/bandit25.html

Solve this level by connecting to port 30002 on localhost and giving it the right password. The password is a combination of the last level’s password and a random 4 digit pin. The seq command is useful for brute-forcing passwords.

Connect to the level with ssh:

ssh bandit24@bandit.labs.overthewire.org -p 2220

Here’s the command that finds you the password. This filters out any lines that contain the string Wrong!. That way, you won’t get overwhelmed with wrong password messages.

seq -f "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX %4g" 0 9999 |
    socat STDIO TCP4:localhost:30002 |
    grep -v Wrong!

After a while, this is what you should see once you send it the right code:

I am the pincode checker for user bandit25. Please enter the password for user bandit24 and the secret pincode on a single line, separated by a space.
Correct!
The password of user bandit25 is YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY

Note the password and use it in the next level.

Bandit level 26

Link to this level: https://overthewire.org/wargames/bandit/bandit26.html

To solve this level, you need to find a way to escape the bandit26 user’s default shell. First, connect to the bandit25 user and see what shell bandit26 is using:

ssh bandit25@bandit.labs.overthewire.org -p 2220

This command filters out the bandit25 and bandit26 users from the password file located at /etc/passwd:

cat /etc/passwd | grep -e bandit25 -e bandit26

The bandit25 user’s shell is /bin/bash, which is a regular shell. The bandit26 user uses an uncommon shell located at /usr/bin/showtext. See the two user’s entries in the /etc/passwd file here:

bandit25:x:11025:11025:bandit level 25:/home/bandit25:/bin/bash
bandit26:x:11026:11026:bandit level 26:/home/bandit26:/usr/bin/showtext

To confirm what content the shell at /usr/bin/showtext has, examine the file using ls and cat like so:

ls -la /usr/bin/showtext

Here you can see that /usr/bin/showtext is a 58 byte large executable file:

-rwxr-xr-x 1 root root 58 Jul 17 15:57 /usr/bin/showtext

Show the contents of /usr/bin/showtext using cat /usr/bin/showtext:

#!/bin/sh

export TERM=linux

exec more ~/text.txt
exit 0

All that the /usr/bin/showtext command does is read out the contents of a file called text.txt in the current user’s directory. Note that you can change what the tilde ~ character expands to by passing a different $HOME variable.

Try running the /usr/bin/showtext program while logged in as bandit25:

bandit25@bandit:~$ /usr/bin/showtext
more: cannot open /home/bandit25/text.txt: No such file or directory
bandit25@bandit:~$ HOME=/home/bandit26 /usr/bin/showtext
more: cannot open /home/bandit26/text.txt: Permission denied

/usr/bin/showtext doesn’t work since you don’t have a text.txt file in your own home directory. Changing the home directory to be the bandit26 user’s home directory doesn’t solve it either, since you don’t have access to the bandit26 user’s data in their home directory.

Inspect your home directory with ls $HOME and find the SSH key for the bandit26 user:

bandit25@bandit:~$ ls $HOME
bandit26.sshkey

Print the contents of this file using cat $HOME/bandit26.sshkey:

-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEApis2AuoooEqeYWamtwX2k5z9uU1Afl2F8VyXQqbv/LTrIwdW
...
IZdtF5HXs2S5CADTwniUS5mX1HO9l5gUkk+h0cH5JnPtsMCnAUM+BRY=
-----END RSA PRIVATE KEY-----

Copy the contents of this file to your computer into a file called bandit26.sshkey. Change this file’s permissions so that only you can read it:

# Assuming you use the wayland clipboard
wl-paste > bandit26.sshkey
chmod 400 bandit26.sshkey

Connect to the bandit26 user with the SSH key at bandit26.sshkey

ssh -i bandit26.sshkey bandit26@bandit.labs.overthewire.org -p 2220

All that the /usr/bin/showtext command does is show the contents of a short text file using the more command. The connection then terminates immediately because more quits after printing all text.

[...]
  _                     _ _ _   ___   __
 | |                   | (_) | |__ \ / /
 | |__   __ _ _ __   __| |_| |_   ) / /_
 | '_ \ / _` | '_ \ / _` | | __| / / '_ \
 | |_) | (_| | | | | (_| | | |_ / /| (_) |
 |_.__/ \__,_|_| |_|\__,_|_|\__|____\___/
Connection to bandit.labs.overthewire.org closed.

There’s a way to trick the more command into not quitting immediately. While more is running, you can make it read other files, open an editor or execute commands.

Make your terminal small and connect to the bandit26 user again. If you are using a tiling window manager, making the terminal float can help as well.

ssh -i bandit26.key bandit26@bandit.labs.overthewire.org -p 2220

While more is running, press vv to enter the vim editor. Inside vim type :e /etc/bandit_pass/bandit26<ENTER>.

You can now see the password:

YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY

You can even enter a full shell inside the vim editor using the following:

set shell=/bin/sh
:shell

Note the password and use it in the next level.

Bandit level 27

Link to this level: https://overthewire.org/wargames/bandit/bandit27.html

Solve this level by using the same trick as in the previous level. Connect using SSH:

ssh bandit26@bandit.labs.overthewire.org -p 2220

Make your terminal small so that the more command doesn’t quit immediately. When you’re inside more, press vv to open vim. Inside vim enter the following command:

:set shell=/bin/sh<CR>:shell

This launches a shell. Run the following inside the shell to print the next level’s password:

./bandit27-do cat /etc/bandit_pass/bandit27
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Note the password and use it to connect to the next level.

Bandit level 28

Link to this level: https://overthewire.org/wargames/bandit/bandit28.html

This level was a lot of fun to solve. It’s good to keep in mind how much information can leak through git repositories.

Connect to the level using ssh:

ssh bandit27@bandit.labs.overthewire.org -p 2220

Go into a temporary directory that you create using the mktemp -d command. Inside the temporary directory, clone the git repository at the following address:

ssh://bandit27-git@localhost:2220/home/bandit27-git/repo

Here are the commands you can run to achieve this.

cd $(mktemp -d)
git clone ssh://bandit27-git@localhost:2220/home/bandit27-git/repo

List all files in the repository that you have checked out using the find command:

find repo

You should see a README file listed among the files:

[...]
repo/README

Read out the contents of the README file using the cat command:

cat repo/README

Note the password contained in the README file and use it in the next level.

The password to the next level is: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Bandit level 29

Link to this level: https://overthewire.org/wargames/bandit/bandit29.html

Like in the last level, you have to find credentials in a git repository that someone forgot to take out. Connect to the level using ssh:

ssh bandit28@bandit.labs.overthewire.org -p 2220

Once you are connected, check out the repository and list its contents using the following commands:

# Create a temporary directory and change into it
cd $(mktemp -d)
# Clone the repository
git clone ssh://bandit28-git@localhost:2220/home/bandit28-git/repo
# List the contents
ls -la repo
total 16
drwxrwxr-x 3 bandit28 bandit28 4096 Aug 31 01:26 .
drwx------ 3 bandit28 bandit28 4096 Aug 31 01:26 ..
drwxrwxr-x 8 bandit28 bandit28 4096 Aug 31 01:26 .git
-rw-rw-r-- 1 bandit28 bandit28  111 Aug 31 01:26 README.md

This level has a README.md file just like in level 28. Run cat on it to check if it contains any credentials:

cat repo/README.md

Someone removed the credentials in the README.md file and replaced them with x characters.

# Bandit Notes
Some notes for level29 of bandit.

## credentials

- username: bandit29
- password: xxxxxxxxxx

If someone removed the password, perhaps the password was in the repository before? Use git log -p on the README.md file to list all changes to it.

git log -p README.md

If you scroll down a bit, you should see the previous password like here:

[...]
commit 73f5d0435070c8922da12177dc93f40b2285e22a
Author: Morla Porla <morla@overthewire.org>
Date:   Wed Jul 17 15:57:30 2024 +0000

    add missing data

diff --git a/README.md b/README.md
index 7ba2d2f..d4e3b74 100644
--- a/README.md
+++ b/README.md
@@ -4,5 +4,5 @@ Some notes for level29 of bandit.
 ## credentials

 - username: bandit29
-- password: <TBD>
+- password: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
[...]

Note the password and use it in the next level.

Bandit level 30

Link to this level: https://overthewire.org/wargames/bandit/bandit30.html

Again, this level is a git repository forensics challenge. Connect to it using ssh:

ssh bandit29@bandit.labs.overthewire.org -p 2220

Clone the repository into a temporary directory:

cd $(mktemp -d)
git clone ssh://bandit29-git@localhost:2220/home/bandit29-git/repo

Inside the repository, search for the password in the README.md file. This time, it’s somewhere else:

bandit29@bandit:/tmp/tmp.T3dxdml6c7$ cd repo
bandit29@bandit:/tmp/tmp.T3dxdml6c7/repo$ ls -a
.  ..  .git  README.md
bandit29@bandit:/tmp/tmp.T3dxdml6c7/repo$ cat README.md
# Bandit Notes
Some notes for bandit30 of bandit.

## credentials

- username: bandit30
- password: <no passwords in production!>

Even checking the git log for the README.md file doesn’t reveal any credentials. Try it yourself:

git log -p

Here’s the transcript:

...

commit 5a53eb83a43bac1f0b4e223e469b40ef68a4b6e6
Author: Ben Dover <noone@overthewire.org>
Date:   Wed Jul 17 15:57:31 2024 +0000

    initial commit of README.md

diff --git a/README.md b/README.md
new file mode 100644
index 0000000..2da2f39
--- /dev/null
+++ b/README.md
@@ -0,0 +1,8 @@
+# Bandit Notes
+Some notes for bandit30 of bandit.
+
+## credentials
+
+- username: bandit29
+- password: <no passwords in production!>
+

It looks like the password was never committed in the first place. Maybe the password is in another branch. List the names of all git branches using the following command:

git branch -a

The git repository contains the following branches:

* master
  origin
  remotes/origin/HEAD -> origin/master
  remotes/origin/dev
  remotes/origin/master
  remotes/origin/sploits-dev

Going through each branch, you should finally find the password in the origin/dev branch:

git log -p origin/dev

Here’s the password in the first commit in the origin/dev branch:

commit eef534022d1ecc3b41d6de068501c4db4154b2c7 (origin/dev)
Author: Morla Porla <morla@overthewire.org>
Date:   Wed Jul 17 15:57:31 2024 +0000

    add data needed for development

diff --git a/README.md b/README.md
index 1af21d3..bc6ad3d 100644
--- a/README.md
+++ b/README.md
@@ -4,5 +4,5 @@ Some notes for bandit30 of bandit.
 ## credentials

 - username: bandit30
-- password: <no passwords in production!>
+- password: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

# Rest of git log -p origin/dev abbreviated

Note the password and use it to connect to level 31.

Bandit level 31

Link to this level: https://overthewire.org/wargames/bandit/bandit31.html

This is another git repository forensics challenge. Connect to the level using ssh:

ssh bandit30@bandit.labs.overthewire.org -p 2220

Clone the repository using the following commands:

cd $(mktemp -d)
git clone ssh://bandit30-git@localhost:2220/home/bandit30-git/repo
cd repo

This time, the password is neither in the git log for the main branch or any other branch. Instead, take a look at the git tags using git tag -l:

git tag -l
# This lists one tag called `secret`

This lists one git tag called secret. Show the git commit that belongs to this tag by running the following command:

git show secret
#> XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Note the password and use it in the next level.

Bandit level 32

Link to this level: https://overthewire.org/wargames/bandit/bandit32.html

To solve this level, you need to create a specific commit and send it to a git remote. When the git remote receives the correct file, it gives you the password for the next level. Connect to this level using ssh.

ssh bandit31@bandit.labs.overthewire.org -p 2220

Clone the repository into a temporary directory:

cd $(mktemp -d)
git clone ssh://bandit31-git@localhost:2220/home/bandit31-git/repo
cd repo

Create a file called key.txt and commit it to your local repository:

cat > key.txt <<EOF
May I come in?
EOF
git add -f key.txt
git commit -m yo

Push your changes to origin with the following git push invocation:

git push origin master

The git commit hook in origin takes over and gives you the password for the next level.

                         _                     _ _ _
                        | |__   __ _ _ __   __| (_) |_
                        | '_ \ / _` | '_ \ / _` | | __|
                        | |_) | (_| | | | | (_| | | |_
                        |_.__/ \__,_|_| |_|\__,_|_|\__|


                      This is an OverTheWire game server.
            More information on http://www.overthewire.org/wargames

bandit31-git@localhost's password:
Enumerating objects: 4, done.
Counting objects: 100% (4/4), done.
Delta compression using up to 2 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 317 bytes | 317.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
remote: ### Attempting to validate files... ####
remote:
remote: .oOo.oOo.oOo.oOo.oOo.oOo.oOo.oOo.oOo.oOo.
remote:
remote: Well done! Here is the password for the next level:
remote: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
remote:
remote: .oOo.oOo.oOo.oOo.oOo.oOo.oOo.oOo.oOo.oOo.
remote:
To ssh://localhost:2220/home/bandit31-git/repo
 ! [remote rejected] master -> master (pre-receive hook declined)
error: failed to push some refs to 'ssh://localhost:2220/home/bandit31-git/repo'

Note the password that the pre-receive hooks prints out and use it in the next level. This might be a good opportunity to double-check your usage of git hooks.

Bandit level 33

Link to this level: https://overthewire.org/wargames/bandit/bandit33.html

In this level, you need to escape a custom shell that turns all commands into uppercase. For example, when you run cat, the shell turns it into a CAT.

Connect to this level using ssh:

ssh bandit32@bandit.labs.overthewire.org -p 2220

Here’s what you’re prompted with when connecting:

[...]
  For more information regarding individual wargames, visit
  http://www.overthewire.org/wargames/

  For support, questions or comments, contact us on discord or IRC.

  Enjoy your stay!

WELCOME TO THE UPPERCASE SHELL

To see what exactly this uppercase shell is, I connected to the previous level and inspect the /etc/passwd password file. Here’s how you can print out the record for the bandit32 user from this level:

grep /etc/passwd -e bandit32

The bandit32 user’s shell is in /home/bandit32/uppershell:

bandit32:x:11032:11032:bandit level 32:/home/bandit32:/home/bandit32/uppershell

It’s not possible to view the contents of uppershell as bandit31. The file belongs to bandit32.

After trying a lot, this is the command that eventually solved the challenge for me:

# Run the name of the current shell as a command
$0

And this is it. I have no idea why this works. Normally, one would suppose that this just runs /home/bandit32/uppershell again.

Print out the password:

cat /etc/bandit_pass/bandit33

Epic laser boom. You’ve solved all levels in the Bandit wargame. Use the password and connect to the next level for an epic win epic message.

Bandit level 34

Link to this level: https://overthewire.org/wargames/bandit/bandit34.html

Connect to this level using ssh:

ssh bandit33@bandit.labs.overthewire.org -p 2220

Print the README.txt file in this level’s home directory using cat README.txt and celebrate:

Congratulations on solving the last level of this game!

At this moment, there are no more levels to play in this game. However, we are constantly working on new levels and will most likely expand this game with more levels soon. Keep an eye out for an announcement on our usual communication channels! In the meantime, you could play some of our other wargames.

If you have an idea for an awesome new level, please let us know!

The Bandit wargame is a great shell command teacher. Give it a try if you are interested in learning how to get weird with the shell. Impress people at parties with obscure knowledge about Bash.

Here are two useful resources that helped me solve the challenges:

Tags

I would be thrilled to hear from you! Please share your thoughts and ideas with me via email.

Back to Index