Skip to main content

OverTheWire | Bandit | Levels 20-29

· 13 min read

Summary:

A write-up providing help with the Bandit wargame on the OverTheWire website, consisting of a series of 32 levels, followed by an additional challenge using an 'uppercase shell' trick at Level 33, that covers various Linux and shell scripting topics. The challenges range from basic navigation to more advanced topics like SSH authentication, file permissions, and Git usage.

Wargame Name: Bandit | Difficulty: Easy | OS: Linux


The aim of this walkthrough is to provide help with the Bandit wargame on the OverTheWire website. Please note that no flags or passwords are directly provided here. Only one of the many ways to solve the challenges.

The wargame is available under Bandit wargame.

High-Level Summary:

  • Level 19-20 | Understanding user ID (UID) and group ID (GID)
  • Level 21 | Using crontab to schedule tasks at specific times
  • Level 22-23 | Understanding cron jobs, shell variables, and environment variables
  • Level 24 | Stealing a password from another user's crontab file
  • Level 25-26 | Understanding SSH public key authentication and stealing passwords
  • Level 27 | Using netcat to establish a connection between two servers
  • Level 28-29 | Understanding the /etc/hosts file and using DNS lookups

Note: Throughout this tutorial/walkthrough the words password and flag are used interchangeably.

Quick intro and start config

Basic idea: finish level x to get level x+1. The general connection via ssh can look like this, where x stands for the current level:

ssh [email protected] -p 2220
SSH connection
Hostbandit.labs.overthewire.org
Port2220

Level 19 -> Level 20

Level Goal: To gain access to the next level, you should use the setuid binary in the homedirectory. Execute it without arguments to find out how to use it. The password for this level can be found in the usual place (/etc/bandit_pass), after you have used the setuid binary.

Solution: Run the executable with the set setuid to read the password file as bandit20 instead of bandit19.

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

Level 20 -> Level 21

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).

NOTE: Try connecting to your own network daemon to see if it works as you think.

Solution: First, we background a netcat listener at an arbitrary port. Then, we use the setuid binary to connect to the same port. Once the flag is sent, we will be provided with a new one.

echo "<previous-flag>" | nc -l localhost 62333 &
...
./suconnect 62333
...

Level 21 -> 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: If you check out the running cron jobs, you will see, that cronjob_bandit22 runs a script every minute. Once you check out the script, you will be provided with the location of the flag/password.

# check out the cron job
cat /etc/cron.d/cronjob_bandit22
...
# check out the script that is run every minute
cat /usr/bin/cronjob_bandit22.sh
...
# get the flag - the file location is indicated in the script
# this is only an example of a temporary file that was generated for me
cat /tmp/t7O6lds9S0RqQh9aMcz6ShpAoZKF7fgv

Level 22 -> 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.

NOTE: Looking at shell scripts written by other people is a very useful skill. The script for this level is intentionally made easy to read. If you are having problems understanding what it does, try executing it to see the debug information it prints.

Solution: Similarly as before, check the cron job, then the script that is run. But here, in order to get to the file, copy the commands that are used in the script to generate the desired file name. Use bandit23 instead of $whoami.

# check out the cron job
cat /etc/cron.d/cronjob_bandit23
...
# check out the script that is run every minute
cat /usr/bin/cronjob_bandit23.sh
...
# run the commands in the script to get the final file name
echo I am user bandit23 | md5sum | cut -d ' ' -f 1
...
# grab the flag in that file
cat /tmp/8ca319486bfbbc3663ea0fbe81326349

Level 23 -> Level 24

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.

NOTE: This level requires you to create your own first shell-script. This is a very big step and you should be proud of yourself when you beat this level!

NOTE 2: Keep in mind that your shell script is removed once executed, so you may want to keep a copy around…

Solution:

# check out the cron job
cat /etc/cron.d/cronjob_bandit24
...
# check out the script that is run every minute
cat /usr/bin/cronjob_bandit24.sh

Here are the contents of the script that is run every minute:

#!/bin/bash

myname=$(whoami)

cd /var/spool/$myname/foo || exit 1
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 -rf ./$i
fi
done

As we can see, the script executes and shortly deletes all the scripts (even the hidden ones) that are located in the /var/spool/bandit23/foo/ directory. Therefore, our task here is to create a script that can read out our next flag.

To do so, first we create a temporary file (where we are the owners of the file). This is were we will save the flag that we read out.

# create a temporary file with owner permissions
mktemp
...
# the output will be our path to our temporary file
# like /tmp/tmp.SfdgYS0COS

Now, head over to the /var/spool/bandit23/foo/ directory to create the script. A very simple example could be something like this:

#!/bin/bash
cat /etc/bandit_pass/bandit24 >> /tmp/tmp.SfdgYS0COS

Right after you saved the file (like grabtheflag.sh), you will have to make it executable.

# give everyone on the machine permissions to execute the file
chmod a+x grabtheflag.sh

Now, all you have to do is wait a bit (less than 60 sec) and check your temporary file for the flag.

Make sure you are fast when saving and making your script executable. Your script will be deleted every 60 seconds.

Level 24 -> Level 25

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. You do not need to create new connections each time

Solution: First, make yourself a temporary directory where you have owner rights.

# create a temporary directory
mktemp -d
...
# head over there
cd <your-temp-dir>

Create a script that writes out all the possible combinations of your flag and the pins into a file. It could look like this:

#!/bin/bash

# flag for level 24
flag="<your-previous-flag>"

# from 10000 on to 0
for i in {10000..0..-1}
do
echo "$flag $i" >> flags.txt
done

Run your script to generate the flag and pin combinations after you made it executable.

# make your script executable
chmod a+x <your-script-name>
# run your script
./<your-script-name>

Then, we use netcat to open up a tcp connection to our desired port (30002) and we feed the connection our flag and pin pairs. The output is redirected into the output.txt file so that we can easily search for our return flag.

cat flags.txt | nc -vN localhost 30002 > output.txt

Make sure you clean up after yourself after you grabbed the flag.

# move back to your home dir
cd /home/bandit24
# remove your temporary directory and it's files
rm -rv <your-temp-dir>

Level 25 -> Level 26

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: The very first thing you should always do is do a quick recon. Simply put, check out your current directory, your user... Once you have done that, it gets pretty clear, that we are provided with bandit26's private ssh key. The file is sitting in your home directory.

Then, following the task description, we check out which shell bandit26 is using.

cat /etc/passwd | grep "bandit26"

Once we head over and check the file, this is how it looks like:

# file /usr/bin/showtext

#!/bin/sh

export TERM=linux

exec more ~/text.txt
exit 0

In a nutshell, all it does is, that once we log in, instead of the usual bash shell, the more command is run on the ~/text.txt file. Then, once the file is read fully, we simply exit.

So if we try to log in as usual with bandit26' private ssh key, we will automatically logged out. To avoid this, we have to make sure, that when the more command reads the file, it can't read it entirely.

We can do this by simply resizing our terminal window when we ssh in as bandit26. Once we are stuck in more, by simply pressing v, we use the subcommand in more to start the vi editor. This will land us inside the vi editor, editing the ~/text.txt file.

The good news is, we can resize our window to actually see something again. The bad news, now we have to escape vi too.

Now, usually we could simply write :![command] to run shell commands from inside the vi editor, but since our shell is still configured to /usr/bin/showtext, we would get back straight to where we started.

To avoid that, we use set shell command inside of vi to specify which shell we want to use. To specify the default bash shell, use the following command:

# inside of the vi editor
:set shell=/bin/bash

Once we specified the shell we want to use, we can simply grab the flag by running

# inside of the vi editor
:! cat /etc/bandit_pass/bandit26

inside the vi editor. To escape the vi editor and land with the usual bash shell, you can simply write :shell into vi.

Level 26 -> Level 27

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

Solution: Since we already have a usual shell connection, once we do our first recon, we will see that there is an executable named bandit27-do lying in our home directory.

Running it without any options/flags will provide us with the program's basic usage.

# running the executable
./bandit27-do
Run a command as another user.
Example: ./bandit27-do id

Since it has the suid set, we can use it's elevated permissions to grab the next flag.

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

Level 27 -> Level 28

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: One of the solutions is to simply clone the whole repository to your local machine.

# on your local machine - clone the whole repository
git clone ssh://[email protected]:2220/home/bandit27-git/repo

Once you check out the cloned files, you will find the next flag inside the README file.

Level 28 -> Level 29

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: Repeat the cloning done on the previous level. Once done, look around. This time the README.md file looks like this:

# Bandit Notes
Some notes for level29 of bandit.

## credentials

- username: bandit29
- password: xxxxxxxxxx


So no luck there. But there is something interesting in the git logs.

# get the git logs
git log
commit 899ba88df296331cc01f30d022c006775d467f28 (HEAD -> master, origin/master, origin/HEAD)
Author: Morla Porla <[email protected]>
Date: Sun Apr 23 18:04:39 2023 +0000

fix info leak

commit abcff758fa6343a0d002a1c0add1ad8c71b88534
Author: Morla Porla <[email protected]>
Date: Sun Apr 23 18:04:39 2023 +0000

add missing data

commit c0a8c3cf093fba65f4ee0e1fe2a530b799508c78
Author: Ben Dover <[email protected]>
Date: Sun Apr 23 18:04:39 2023 +0000

initial commit of README.md

The last commit message indicates that there was an info leak before. To check out the changes pushed by that commit, use the git show command with the appropriate commit hash.

# show the git message log and textual diff
git show 899ba88df296331cc01f30d022c006775d467f28
commit 899ba88df296331cc01f30d022c006775d467f28 (HEAD -> master, origin/master, origin/HEAD)
Author: Morla Porla <[email protected]>
Date: Sun Apr 23 18:04:39 2023 +0000

fix info leak

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

- username: bandit29
-- password: <flag>
+- password: xxxxxxxxxx


As you can see, the next flag/password is easily readable by simple checking out the differences between the two commits. So instead of the git show command, we could also have used the git diff command like so:

git diff 899ba88df296331cc01f30d022c006775d467f28 abcff758fa6343a0d002a1c0add1ad8c71b88534