OverTheWire’s wargames are offered to help learn and practice security concepts in the form of fun-filled games.

The Bandit wargame is aimed at absolute beginners and will teach them the basics needed to be able to play other wargames. All the challenges are focused on the Linux systems and their commands. It aims to get the player familiar with the Linux terminal and introduce some basic security concepts.

It consists of a total of 33 Levels. At each level, you have to find the password for the next level to continue playing. Each Level is a user that you connect as, using SSH in the bandit.labs.overthewire.org server.

In this post, we will present the solutions for Levels 20-29 of the Bandit wargame.

NOTE: These walkthroughs are written and published to help other members of the community that are stuck at some Level. It is strongly advised to first try the challenges yourself until you can progress no more, and only then come back here to see the solution.

Level 20

Level Goal

There is a setuid binary in the homedirectory that does the following: it makes a connection to localhost on the port you specify as a commandline argument. It then reads a line of text from the connection and compares it to the password in the previous level (bandit20). If the password is correct, it will transmit the password for the next level (bandit21).

Solution

Log in to bandit20, using the password found from Level 19:

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

We first use Netcat to create a simple command-line messaging server instantly in order for the suconnect setuid binary to connect to:

nc -l -p 42000

In another tab after we have logged in as bandit20, we run the binary with the port we have selected:

./suconnect 42000

From the other tab (nc server) we enter the current level’s password:

bandit20@bandit:~$ nc -l -p 42000
GbKksEFF4yrVs6il55v6gwY5aVje5f0j

And we have the following output from suconnect:

bandit20@bandit:~$ ./suconnect 42000
Read: GbKksEFF4yrVs6il55v6gwY5aVje5f0j
Password matches, sending next password

And the password is sent to our nc server:

bandit20@bandit:~$ nc -l -p 42000
GbKksEFF4yrVs6il55v6gwY5aVje5f0j
gE269g2h3mw3pwgrj0Ha9Uoqen1c9DGr

Level 21

Level Goal

A program is running automatically at regular intervals from cron, the time-based job scheduler. Look in /etc/cron.d/ for the configuration and see what command is being executed.

Solution

Log in to bandit21, using the password found from Level 20:

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

We first find all the cron jobs in /etc/cron.d:

bandit21@bandit:~$ ls /etc/cron.d
cronjob_bandit15_root  cronjob_bandit17_root  cronjob_bandit22  cronjob_bandit23  cronjob_bandit24  cronjob_bandit25_root

We are interested in the cronjob_bandit22 job, so we cat it:

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

It runs the script /usr/bin/cronjob_bandit22.sh on reboot, so we cat the script in order to see what it does:

bandit21@bandit:~$ cat /usr/bin/cronjob_bandit22.sh
#!/bin/bash
chmod 644 /tmp/t7O6lds9S0RqQh9aMcz6ShpAoZKF7fgv
cat /etc/bandit_pass/bandit22 > /tmp/t7O6lds9S0RqQh9aMcz6ShpAoZKF7fgv

It’s just redirects the bandit22 password in a file in the /tmp directory, so we just cat its contents:

bandit21@bandit:~$ cat /tmp/t7O6lds9S0RqQh9aMcz6ShpAoZKF7fgv
Yk7owGAcWjwMVRwrTesJEwB7WVOiILLI

Level 22

Level Goal

A program is running automatically at regular intervals from cron, the time-based job scheduler. Look in /etc/cron.d/ for the configuration and see what command is being executed.

Solution

Log in to bandit22, using the password found from Level 21:

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

We first find all the cron jobs in /etc/cron.d:

bandit22@bandit:~$ ls /etc/cron.d
cronjob_bandit15_root  cronjob_bandit17_root  cronjob_bandit22  cronjob_bandit23  cronjob_bandit24  cronjob_bandit25_root

We are interested in the cronjob_bandit23 job, so we cat it:

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

It runs the script /usr/bin/cronjob_bandit23.sh on reboot, so we cat the script in order to see what it does:

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

The script gets the current users name and created the phrase, e.g. I am user bandit23. It then passes this phrase through md5sum and produces a hash.

In order to find the password for user bandit23 we do:

bandit22@bandit:~$ echo I am user bandit23 | md5sum | cut -d ' ' -f 1
8ca319486bfbbc3663ea0fbe81326349

That is the name of the file in the /tmp directory that it saves the password, so we just cat it:

bandit22@bandit:~$ cat /tmp/8ca319486bfbbc3663ea0fbe81326349
jc1udXuA1tiHqjIsL8yaapX5XIAI6i0n

Level 23

Level Goal

A program is running automatically at regular intervals from cron, the time-based job scheduler. Look in /etc/cron.d/ for the configuration and see what command is being executed.

Solution

Log in to bandit23, using the password found from Level 22:

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

We first find all the cron jobs in /etc/cron.d:

bandit22@bandit:~$ ls /etc/cron.d
cronjob_bandit15_root  cronjob_bandit17_root  cronjob_bandit22  cronjob_bandit23  cronjob_bandit24  cronjob_bandit25_root

We are interested in the cronjob_bandit24 job, so we cat it:

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

It runs the script /usr/bin/cronjob_bandit24.sh on reboot, so we cat the script in order to see what it does:

bandit23@bandit:~$ cat /usr/bin/cronjob_bandit24.sh
#!/bin/bash

myname=$(whoami)

cd /var/spool/$myname
echo "Executing and deleting all scripts in /var/spool/$myname:"
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

We can write a script of our own in order to get executed by bandit24 after 1 minute from /usr/bin/cronjob_bandit24.sh.

We first create a directory to work on, in /tmp, in order to work:

mkdir /tmp/mine23
cd /tmp/mine23

Then we create a script:

vim level23.sh

And add the following in order to copy the password from /etc/bandit_pass/bandit24 to our directory:

#!/bin/bash
cat /etc/bandit_pass/bandit24 > /tmp/mine23/bandit24

We have to change the permissions in order to be executable be everyone:

chmod 777 level23.sh

And we need to create the file for the password and make it writable for everyone:

touch bandit24
chmod 666 bandit24

We then copy the script to /var/spool/bandit24 and wait for 1 minute to be run by cron:

cp level23.sh /var/spool/bandit24

After 1 minute we cat the file:

bandit23@bandit:/tmp/mine23$ cat bandit24
UoMYTrfrBFHyQXmg6gzctqAwOmw1IohZ

Level 24

Level Goal

A daemon is listening on port 30002 and will give you the password for bandit25 if given the password for bandit24 and a secret numeric 4-digit pincode. There is no way to retrieve the pincode except by going through all of the 10000 combinations, called brute-forcing.

Solution

Log in to bandit24, using the password found from Level 23:

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

We can connect to the service with nc:

nc localhost 30002

And try to enter the current level password and a random code:

bandit24@bandit:~$ nc localhost 30002
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.
UoMYTrfrBFHyQXmg6gzctqAwOmw1IohZ 0000
Wrong! Please enter the correct pincode. Try again.

We can brute-force our login by testing all 4-digit combinations with a simple script.

We first create a directory to work on, in /tmp, in order to work:

mkdir /tmp/mine24
cd /tmp/mine24

Then we create a script:

vim level24.sh

And append the following:

#!/bin/bash

for i in {0000..9999};
do
    echo "UoMYTrfrBFHyQXmg6gzctqAwOmw1IohZ $i"
done | nc localhost 30002

Make the script executable:

chmod +x level24.sh

And run the script and save the output to a file:

./level24.sh > out.txt

Now view only the line with the correct password:

bandit24@bandit:/tmp/mine24$ grep Wrong out.txt -v
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 uNG9O58gUE7snukf3bvZ0rxhtnjzSGzG

Exiting.

Level 25

Level Goal

Logging in to bandit26 from bandit25 should be fairly easy… The shell for user bandit26 is not /bin/bash, but something else. Find out what it is, how it works and how to break out of it.

Solution

Log in to bandit25, using the password found from Level 24:

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

We can first try to login using the bandit26.sshkey private SSH key for the bandit26 user:

bandit25@bandit:~$ ssh -i bandit26.sshkey bandit26@localhost
# ...
  Enjoy your stay!

  _                     _ _ _   ___   __
 | |                   | (_) | |__ \ / /
 | |__   __ _ _ __   __| |_| |_   ) / /_
 | '_ \ / _` | '_ \ / _` | | __| / / '_ \
 | |_) | (_| | | | | (_| | | |_ / /| (_) |
 |_.__/ \__,_|_| |_|\__,_|_|\__|____\___/
Connection to localhost closed.

We just get disconnected every time we try to login.

We can find the default shell for bandit26 user:

cat /etc/passwd | grep bandit26

And we see that the default shell is /usr/bin/showtext:

bandit25@bandit:~$ cat /etc/passwd | grep bandit26
bandit26:x:11026:11026:bandit level 26:/home/bandit26:/usr/bin/showtext

We can cat this script to see what it does:

bandit25@bandit:~$ cat /usr/bin/showtext
#!/bin/sh

export TERM=linux

more ~/text.txt
exit 0

We can see that it uses more to show the ~/text.txt file that contains some ascii art. If we can use a terminal that can zoom very close or make the terminal window very small so that the ascii art will be buffered, we can use more to execute commands with a !.

!whoami
bandit25
------------------------

But that did not work in this case.

We can also use more to open a file in vim by pressing v, and then we can run in vim the command (by pressing :):

r /etc/bandit_pass/bandit26

In order to open the password file for reading.

  1   _                     _ _ _   ___   __
  2 5czgV9L3Xx8JPOyRbXh6lQbmIOWvPT6Z
  3  | |                   | (_) | |__ \ / /
  4  | |__   __ _ _ __   __| |_| |_   ) / /_
  5  | '_ \ / _` | '_ \ / _` | | __| / / '_ \
  6  | |_) | (_| | | | | (_| | | |_ / /| (_) |
  7  |_.__/ \__,_|_| |_|\__,_|_|\__|____\___/

Level 26

Level Goal

Good job getting a shell! Now hurry and grab the password for bandit27!

Solution

Log in to bandit26, using the password found from Level 25:

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

Even though we have a password we cannot enter a shell on user bandit26. What we can do, is the same as in the previous level, we have to shrink the terminal window in order for more to buffer, and we can open the file in vim by pressing v, and then we can run in vim a command (by pressing :) to set our shell to be /bin/bash:

:set shell=/bin/bash
:shell

Now we have a shell for user bandit26.

If we ls, we see the following files:

bandit26@bandit:~$ ls
bandit27-do  text.txt

If we run it, we see:

bandit26@bandit:~$ ./bandit27-do
Run a command as another user.
  Example: ./bandit27-do id

So we just cat to see the password we want:

bandit26@bandit:~$ ./bandit27-do cat /etc/bandit_pass/bandit27
3ba3118a22e93127a4ed485be72ef5ea

Level 27

Level Goal

There is a git repository at ssh://bandit27-git@localhost/home/bandit27-git/repo. The password for the user bandit27-git is the same as for the user bandit27. Clone the repository and find the password for the next level.

Solution

Log in to bandit27, using the password found from Level 26:

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

We first create a directory to work on, in /tmp, in order to work:

mkdir /tmp/mine27
cd /tmp/mine27

We then clone the git repository:

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

The password is the same as the current’s level.

There is only one README file in the repo and we can cat it:

bandit27@bandit:/tmp/mine27$ cat repo/README
The password to the next level is: 0ef186ac70e04ea33b4c1853d2526fa2

Level 28

Level Goal

There is a git repository at ssh://bandit28-git@localhost/home/bandit28-git/repo. The password for the user bandit28-git is the same as for the user bandit28. Clone the repository and find the password for the next level.

Solution

Log in to bandit28, using the password found from Level 27:

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

We first create a directory to work on, in /tmp, in order to work:

mkdir /tmp/mine28
cd /tmp/mine28

We then clone the git repository:

git clone ssh://bandit28-git@localhost/home/bandit28-git/repo

The password is the same as the current’s level.

There is only one README file in the repo and we can cat it:

bandit28@bandit:/tmp/mine28$ cd repo
bandit28@bandit:/tmp/mine28/repo$ cat README.md
# Bandit Notes
Some notes for level29 of bandit.

## credentials

- username: bandit29
- password: xxxxxxxxxx

We can git log in order to see all previous commits:

bandit28@bandit:/tmp/mine28/repo$ git log
commit edd935d60906b33f0619605abd1689808ccdd5ee
Author: Morla Porla <morla@overthewire.org>
Date:   Thu May 7 20:14:49 2020 +0200

    fix info leak

commit c086d11a00c0648d095d04c089786efef5e01264
Author: Morla Porla <morla@overthewire.org>
Date:   Thu May 7 20:14:49 2020 +0200

    add missing data

commit de2ebe2d5fd1598cd547f4d56247e053be3fdc38
Author: Ben Dover <noone@overthewire.org>
Date:   Thu May 7 20:14:49 2020 +0200

    initial commit of README.md

We can checkout to the add missing data commit by using the commit hash:

git checkout c086d11a00c0648d095d04c089786efef5e01264

And then if we cat the README.md:

bandit28@bandit:/tmp/mine28/repo$ cat README.md
# Bandit Notes
Some notes for level29 of bandit.

## credentials

- username: bandit29
- password: bbc96594b4e001778eee9975372716b2

Level 29

Level Goal

There is a git repository at ssh://bandit29-git@localhost/home/bandit29-git/repo. The password for the user bandit29-git is the same as for the user bandit29. Clone the repository and find the password for the next level.

Solution

Log in to bandit29, using the password found from Level 28:

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

We first create a directory to work on, in /tmp, in order to work:

mkdir /tmp/mine29
cd /tmp/mine29

We then clone the git repository:

git clone ssh://bandit29-git@localhost/home/bandit29-git/repo

The password is the same as the current’s level.

There is only one README file in the repo and we can cat it:

bandit29@bandit:/tmp/mine29$ cd repo
bandit29@bandit:/tmp/mine29/repo$ cat README.md
# Bandit Notes
Some notes for bandit30 of bandit.

## credentials

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

If we try all the other commits we will show that no password is shown. But, if we list all branches (and remotes), we will see:

bandit29@bandit:/tmp/mine29/repo$ git branch -a
* master
  remotes/origin/HEAD -> origin/master
  remotes/origin/dev
  remotes/origin/master
  remotes/origin/sploits-dev

So we can try to see the contents of README.md from the other branches, and we can do that without checkout, but by simply using dif:

bandit29@bandit:/tmp/mine29/repo$ git diff remotes/origin/dev
diff --git a/README.md b/README.md
index 39b87a8..1af21d3 100644
--- a/README.md
+++ b/README.md
@@ -4,5 +4,5 @@ Some notes for bandit30 of bandit.
 ## credentials

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