Bandit Challenge

bandit picture

Overview of the Bandit Challenge

Bandit Challenge Description

Bandit is the entry-level wargame on OverTheWire designed to teach beginners the basics of using the Linux command line. It focuses on fundamental skills such as file navigation, file manipulation, and simple scripting. The game is structured into multiple levels, each presenting a unique challenge that requires players to use various Linux commands and techniques to retrieve a password needed to access the next level. Bandit is an excellent starting point for anyone new to the command line or looking to solidify their basic skills.

General Structure

  1. Levels: The Bandit wargame consists of over 30 levels. Each level is accessible via SSH, and players must solve the current level's challenge to obtain the password for the next level.
  2. Challenges: Each level's challenge is designed to teach a specific command or concept related to Linux. Challenges range from locating hidden files to manipulating text, understanding file permissions, and using basic scripting.
  3. Tools and Commands: Players will become familiar with a variety of essential Linux commands and tools, including but not limited to:
    • ls, cd, cat, file, find, grep, sort, uniq, strings, chmod, ssh, scp, and simple bash scripting.

Learning Outcomes

  • Command Line Proficiency: Gain confidence and proficiency with the Linux command line, essential for any cybersecurity or IT professional.
  • Problem-Solving Skills: Develop logical thinking and problem-solving skills by tackling progressively more difficult challenges.
  • Practical Knowledge: Learn how to navigate the file system, manipulate files, and understand file permissions, all fundamental skills for any computer user.
  • Preparation for Advanced Challenges: Build a solid foundation that prepares players for more advanced wargames on OverTheWire and other cybersecurity challenges.

Bandit is an ideal starting point for beginners and serves as a gateway to more complex challenges in the world of cybersecurity. It provides a hands-on, practical approach to learning that is both engaging and educational.

Where to start?

You can go there for the solution to the first challenge

Acknowledgments

To prepare that walkthrough I decided to use another solution to keep me focused and to ensure I wasn't missing any important information, so here it is if by any mean you speak french and you'd like to check it out.

Bandit0

Level Goal

The goal of this level is for you to log into the game using SSH.
The host to which you need to connect is bandit.labs.overthewire.org, on port 2220.
The username is bandit0 and the password is bandit0.

Commands useful to solve the level

Helpful Reading Material

Where to start?

We know that we need to use ssh to log into the game and already know that there is only one command that may be useful to solve the challenge. After reading about what is the secure shell on wikipedia, let's dive right into it and look into the ssh man page

Part 1 : Host Specification

Our first job is to find out how to specify the host that we're trying to connect to.

Hint

Look in the ssh man page, in the DESCRIPTION section, right after the SYNOPSIS there should be, near the beginning, the name of an item that could already be found in the SYNOPSIS section

Solution

The argument we are looking for is the one name destination this argument is the host we are trying to connect to.
For now, our command looks like : ssh bandit.labs.overthewire.org

Part 2 : Port Specification

After running this command, we can see the following prompt in the terminal :


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

!!! You are trying to log into this SSH server on port 22, which is not intended.

Charystag@bandit.labs.overthewire.org: Permission denied (publickey).

So we need to use the port that was specified in the challenge rules

Hint

Try to look again in the SYNOPSIS and DESCRIPTION sections of the ssh man page and see if you can manage to find how to specify a port to connect to the remote host

Solution

Using the -p option allows us to specify a port to connect to. Our updated command ends up looking like this :

ssh -p 2220 bandit.labs.overthewire.org

:bulb: It is a good practice to put all option arguments before any non-option argument

Part 3 : Username Specification

Now that we specified the port to connect to, we can see the following prompt :

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

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

!!! You are trying to log into this SSH server on port 2220 with a username
!!! that does not match the bandit game.

Charystag@bandit.labs.overthewire.org's password: 

and when we try to input the provided password : bandit0, we get the following response :


Permission denied, please try again.
Charystag@bandit.labs.overthewire.org's password: 

The important information is : with a username that does not match the bandit game. This tells us that we'll need to specify our username to successfully connect to level bandit0

Hint

Once again, you have to look into the sections SYNOPSIS and DESCRIPTION of the ssh man page.
The argument you are looking for is now one that allows you to log in as a given user on a remote machine.

Solution

Using the -l option allows us to specify the user that we want to log into on the remote machine.
Our full command looks like : ssh -p 2220 -l bandit0 bandit.labs.overthewire.org. Once we get the login prompt, we can now enter the password and successfully login to the first level.

Full Solution

The full command is :

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

Once we get the login prompt, we can then enter the password bandit0 to successfully complete the bandit0 challenge.

You can now solve the first level

Bandit0->1

Level Goal

The password for the next level is stored in a file called readme located in the home directory. Use this password to log into bandit1 using SSH. Whenever you find a password for a level, use SSH (on port 2220) to log into that level and continue the game.

Commands useful to solve the level

Helpful Reading Material

Where to start ?

We know that the password for the next level is stored in a file called readme which is located in the home directory. First thing to know is that when we log as a user we end up in the user home directory. That is how we know that we automatically begin in user bandit0 home directory.

Part 1 : Listing Files

Hint

Why don't you look into the ls man page to ensure that the file you're looking for is really there?

Solution

The command we are looking for is ls. This will allow us to list the directory files and ensure that the file we are looking for is there

Part 2 : Printing File

After running the command ls, you should get this output :

bandit0@bandit:~$ ls
readme
bandit0@bandit:~$

Now we need to know how to print the contents of the readme file

Hint

Same as before, we can look into the cat man page to get to know the cat utility.

Solution

The command we're looking for is cat readme. It will allow us to retrieve the contents of the readme file.

Full Solution

The command cat readme will output a prompt similar to this one

bandit0@bandit:~$ cat readme
password_string
bandit0@bandit:~$

Where password_string is a 33 alphanumeric characters password string.

We can then copy this string with ctrl+shift+c and paste it with ctrl+shift+v

You can now jump to the next level

Bandit1->2

Level Goal

The password for the next level is stored in a file called - located in the home directory

Commands useful to solve the level

Helpful Reading Material

Where to start?

The solution to this challenge is actually written in the Reading Material. However, if you would like more explanations. You can open the Full Solution to the Challenge

Full Solution

After listing the contents of the directory with ls, we can notice the following :

bandit1@bandit:~$ ls
-
bandit1@bandit:~$

This means that the file - is actually there. However, when we try print the contents of the file with cat - we get the following :

bandit1@bandit:~$ cat -

Where cat seems to wait for an input. By running man cat, we notice the following text in the Description section :
With no File, or when File is -, read standard input.
This tells us that cat doesn't interpret - as Filename but as a directive that tells it to read from standard input. It will echo everything you entered on standard input once you press the ENTER key. To tell cat that you're done writting to stdin, you can press ctrl+D (^D).
This is the reason why we need to run cat ./- thus specifying the relative path to the file and not only its name to cat

You can now jump to the next level

Bandit2->3

Level Goal

The password for the next level is stored in a file called spaces in this filename located in the home directory

Commands useful to solve the level

Helpful Reading Material

Where to start?

For this level again, the solution is written in the helpful material. For more explanations, you can read the Walkthrough.

Part 1 : Analysing the directory files

After listing the contents of the directory with ls, we can notice the following :

bandit2@bandit:~$ ls
spaces in this filename
bandit2@bandit:~$

Which seems to indicate that there are 4 files in this directory : spaces, in, this and filename.

Let's look into the ls man page, and try to look for an option that lists one file per line.

Hint

Options can also be numbers

Solution

The -1 option is the option we were looking for

However when using the option we previously found we see the following :

bandit2@bandit:~$ ls -1
spaces in this filename
bandit2@bandit:~$ 

which is exactly the same prompt as before. However, as this option allows us to list one file per line, we know for sure that spaces in this filename is actually the name of a unique file.

Part 2 : Printing the file

Now that we know that the file is called spaces in this filename, we need to refer find a way to print this file.

Hint

By looking into the QUOTING section of the gnu bash manual, can you retrieve all the quoting mechanism that are available to us in order to print this file?

Solution

There are 3 quoting mechanism that allow us to print this file :

  1. By escaping the spaces with the filename : spaces\ in\ this\ filename. As the \ preserves the litteral value of the character immediately following it.

  2. By enclosing the filename within simple quotes : 'spaces in this filename'. As the simple quotes preserve the litteral value of all the characters they enclose

  3. By enclosing the filename within double quotes : "spaces in this filename". As the double quotes preserve the litteral value of all the characters they enclose, appart from $, ` and \. As the character we need to preserve is the space, we can also use the double quotes to achieve this goal.

Full Solution

  1. cat "spaces in this filename" to print the password to stdout.

You can now jump to the next level

Bandit3->4

Level Goal

The password for the next level is stored in a hidden file in the inhere directory.

Commands useful to solve the level

Helpful Reading Material

Where to start?

In this challenge, we're now introduced to 2 new notions : directories and hidden files. The reading material gives valuable information and helps us understand better what actually is a directory and what are hidden files. One key takeaway is that hidden files are not safer that regular files, they are just not listed by default by listing utilities. However, the information that they are not listed by default already gives us two hints :

  1. The file is there
  2. We can access it

Now, let's dive into how we are going to actually view and access the file in this hidden directory.

Let's move, I want to be close to the file I'm looking for

Part 1 : the cd builtin

Let's now meet a new friend, the cd builtin. We will need to use this builtin to navigate to the directory named inhere.

Hint

man cd doesn't work here. Indeed, the cd builtin is part of the shell you're using (I'll assume you're using bash).

However, you can view the SHELL BUILTIN COMMANDS section of the gnu bash manual.

Solution

To effectively change directory to the inhere directory, we need to run the command cd inhere.

Part 2 : listing hidden files

Now that we are in the inhere directory, if we run the ls command, this is the output we get :

bandit3@bandit:~/inhere$ ls
bandit3@bandit:~/inhere$

However, we know that there is a hidden file in this directory, we need to find a way to retrive that file.

Hint

Look at the DESCRIPTION section of ls. The option you're looking for should be near the top

Solution

The -a or --all is the option you're looking for. It allows to not ignore the entries starting with a ..
This is the output we get after listing all of our directory contents :

bandit3@bandit:~/inhere$ ls --all
.  ..  .hidden
bandit3@bandit:~/inhere$

Now that we know that the file we're are looking for, we can print its content with cat .hidden

Full Solution

  1. cd inhere to change directory to the inhere directory
  2. ls --all to print all the contents of the inhere directory
  3. cat .hidden to print the hidden file

Why move ? I can do everything from my home directory

The idea is basically the same than if we wanted to move, with a slight variation.

Part 1 : listing the directory contents

Up until now, we used the ls utility with options but without any argument. We need to find a way to specify a directory to the ls command.

Hint

Once again, we'll look in the ls man page, but this time we need to have a look in the SYNOPSYS section.

Solution

The command ls --all inhere it the command we're looking for. this command will allow us to list the contents of the inhere directory, without moving nor ignoring the hidden files.
Running it gives us the following output :

bandit3@bandit:~$ ls --all inhere
.  ..  .hidden
bandit3@bandit:~$

Part 2 : printing the hidden file

Now that we now that the hidden file in the inhere directory is called .hidden, we can run cat and give it the relative path to the .hidden file as an argument : cat inhere/.hidden. This will dump the password string to stdout

Full Solution

  1. ls --all inhere to list the contents of the inhere directory
  2. cat inhere/.hidden to print the contents of the .hidden file

You can now jump to the next level

Bandit4->5

Level Goal

The password for the next level is stored in the only human-readable file in the inhere directory.
Tip: if your terminal is messed up, try the “reset” command.

Commands useful to solve the level

Helpful Reading Material

Where to start?

As there isn't a lot of helpful reading material (because the material is already very complete), we must start by opening the find(1) documentation

Now that we got an idea of what the find utility does we need to know exactly what characteristics the file we are looking for has.

  • it is human-readable.
  • it is a file

We need to find a way to only for human readable files in a given directory

Part 1 : Finding directory files

During all this level, we are only going to use the find(1) utility. In fact, our solution to this level will consist in one unique call to the find(1) utility. First things first, we need to know how to run the find command to list the directory files

Hint

Look at the starting points section of the find(1) man page.

Solution

When looking in the starting points section of the find(1) man page, we see an optional argument named starting-point. This argument allows the user to specify a starting directory when running the find utility. By default, the starting point is . which is the current directory. So, running find inhere allows us to list the contents of the inhere directory

Part 2 : Listing only the files

Before delving deeper into the find(1) man page to know how we can use find to retrieve the only human-readable file in the inhere directory let's take a closer look at the output we got from running find alone :

bandit4@bandit:~$ find
.
./inhere
./inhere/-file01
./inhere/-file02
./inhere/-file08
./inhere/-file06
./inhere/-file00
./inhere/-file04
./inhere/-file05
./inhere/-file07
./inhere/-file03
./inhere/-file09
./.profile
./.bashrc
./.bash_logout
bandit4@bandit:~$

From that output, we can notice that find lists all the file in the directories and subdirectories, without ignoring hidden files by default. We can notice that it lists all the files and directories contained in the starting point, along with the starting point.

Let's now try to list only the regular files within the inhere directory

Hint

By looking at the section 2 of the gnu findutils documentation can you retrieve a test that tests for regular files?

Solution

The option we're looking for is -type, which allows us to test for the type of file we're looking for. We'll give the -type option the f argument to only look for regular files.
The command we're looking for is find inhere -type f.

Part 3 : Finding the only human readable file

Here is the output from the command find inhere -type f :

bandit4@bandit:~$ find inhere/ -type f
inhere/-file01
inhere/-file02
inhere/-file08
inhere/-file06
inhere/-file00
inhere/-file04
inhere/-file05
inhere/-file07
inhere/-file03
inhere/-file09
bandit4@bandit:~$

We could try to manually run cat on each file but besides being an ugly as hell solution, it presents a security risk. Thankfully, there is a solution which stands in the file(1) utility We now need to find in the file(1) man page how to find the only human readable file within the inhere directory.

Hint

Look into the file(1) and the section 3 of the gnu findutils documentation, see if there is an ACTION in the find(1) page that could let you execute the file command on the file you retrieved

Solution

The ACTION we're looking for is -execdir. We will use the form -execdir command ; as it is safer than the -exec command ; form (see security considerations. As the ; is a metacharacter, we will need to escape it with a \ to pass it to the find command. For the same reason, we'll have to enclose the brackets {} within simple or double quotes

The command we're looking for is : find inhere -type f -execdir file '{}' \;
Here is an output you could get by running this command :

bandit4@bandit:~$ find inhere/ -type f -execdir file "{}" \;
./-file01: data
./-file02: data
./-file08: data
./-file06: data
./-file00: data
./-file04: data
./-file05: data
./-file07: ASCII text
./-file03: data
./-file09: data
bandit4@bandit:~$

We can now run the command cat inhere/-file07 to get the password string

Full Solution

  1. find inhere/ -type f -execdir file "{}" \; to find all the regular files in the inhere directory and run the file utility on them
  2. cat inhere/-file07 to print the contents of the retrieved file

Bonus : One-liner command

Useful commands:

  • bash (or any shell)
  • find
  • file
  • grep
  • cat
Hint

To get you started, if you want to find (no pun intended) by yourself, here are a few informations to get you on the right track.

Here are the find options we'll use to achieve our goal :

  • type
  • execdir (x2)
  • quit (optionnal)
Solution

The command is the following :

find inhere/ -type f -execdir bash -c 'file {} | grep text > /dev/null' \; -execdir cat '{}' \; -quit

Here is a step-by-step overview of the command :

  1. The first execdir calls execute the command file {} | grep text > /dev/null on each retrieved file in the inhere directory.

The redirection to /dev/null is to ensure that nothing gets printed on stdout, but the important thing here is actually the exit status of the grep command. See bash invocation for informations about the -c option.

  1. The second execdir calls cat on the only human-readable file in the inhere directory
  2. The quit option allows us to stop the find utility once we found what we're looking for. To understand what it does, you can replace grep by grep -v in the previous command

You can now jump to the next level

Bandit5->6

Level Goal

The password for the next level is stored in a file somewhere under the inhere directory and has all of the following properties:

  • human-readable
  • 1033 bytes in size
  • not executable

Commands useful to solve the level

Helpful Reading Material

Where to start?

The solution is very similar to the one of the previous level so check that one for a more in-depth explanation. For this level, I will give less explanations and only add complements to teach you about where to find the relevant information. Without further ado, let's dive right into the solution.

Walkthrough

The goal of this exercise is to add options to the find command, so that the file we're retrieving meets all the requirements. We'll try to find the options one after the other into the find(1) documentation.

Option 1 : File Size

The first option we're looking for is an option that allows us to check for the file size. Let's look in the find(1) man page to see if we can find the option we need.

Hint

Try to look in the section 2 of the gnu findutils documentation.

Solution

The option we're looking for is described there. It is the size option.
We are going to invoke it like this : -size 1033c.

Option 2 : Not Executable

The second option we're looking for is an option that allows us to check for the executable permission on the file we encounter. Let's look once again into the find(1) man page (or the gnu findutils documentation) to find what we need.

Hint

This time, we still need to look at the section 2 of the gnu findutils documentation. However, we need to look into two different subsections of this section 2 to complete our option.

Solution

The option we're looking for is described there. It is the executable option. However, we need our file to not be executable, so we can see in this section that to negate this condition we can use the -not operator.
We are going to invoke our option like this : -not -executable.

Building the command

After getting our two options, the rest of the command is exactly the same as with the previous exercise.
Here is our command :

find inhere -type f -size 1033c -not -executable -execdir file '{}' \; -print

We need to print the file after because due to using the execdir option instead of the exec option (see the security considerations)

Security concerns : One-liner from previous exercise

In the previous level I gave you a one-liner to solve the level

find inhere/ -type f -execdir bash -c 'file {} | grep text > /dev/null' \; -execdir cat '{}' \; -quit

Although this command gives the right answer, it presents a security concern. Indeed, if an attacker puts a special filename in your directory, it could lead to the deletion of all of your data. Let's see a safe example right now. Try running the following script and understanding its output (you can copy and paste the script into you terminal window):

#!/usr/bin/env bash
clear #This is to keep only the script outputs in case you copy-paste it to your terminal window
mkdir -p /tmp/testrm
cd "$(mktemp -d)" && echo "Step 1 - Now in temporary directory" || kill -INT $$
echo "Step 2 - creation of the /tmp/testrm directory, that will be useful to bring out our security concern"
if ls /tmp | grep testrm > /dev/null ; then echo /tmp/testrm is still there; else echo /tmp/testrm is unfortunately gone; fi
echo "Step 3 - Creation of two test files : 'bonjour' and 'bonjour ; rm -rf \$TEST'"
touch bonjour 'bonjour ; rm -rf $TEST'
echo -n "These are the directory files : " ; ls -1
echo "Step 4 - Exporting the TEST variable to contain the value of '/tmp/testrm', the directory we want to delete"
export TEST="/tmp/testrm"
echo "Step 5 - We now run the find command and we use the execdir option to call a bash instance which will run \
the file utility on each file we find"
find -execdir bash -c 'file {}' \;
echo -n "Step 6 - We can now see that our test directory has been removed : "
if ls /tmp | grep testrm > /dev/null ; then echo /tmp/testrm is still there; else echo /tmp/testrm is unfortunately gone; fi
cd "-" && rm -rf "$OLDPWD" && echo "Step 7 - Back in $PWD"

In this example we see that our /tmp/testrm directory has been deleted even though we didn't intended at all to do so. This is because the command 'rm -rf' has been executed when we tried to execute file on our dangerously named file without sanitizing the input. Even if it is harmless for this example, if the attacker replaces $TEST with $HOME it could be way more harmful.
To prevent this from hapenning, instead of the command find -execdir bash -c 'file {}' \; we can run the following :

find -execdir bash -c 'file "$@"' bash '{}' \;

to understand precisely what this command do you can go check the -c option in the bash invocation section of the gnu bash manual.

You can now jump to the next level

Bandit6->7

Level Goal

The password for the next level is stored somewhere on the server and has all of the following properties:

  • owned by user bandit7
  • owned by group bandit6
  • 33 bytes in size

Commands useful to solve the level

Helpful Reading Material

See bandit4->5 and bandit5->6 for more useful material.

Where to start?

Let's dive right into the solution, as this level is very similar to the two previous ones.

Walkthrough

We already know about the size option, we only need to find about the options that allow us to filter the files using the user and group owning the file. The only thing missing is the fact that the file lies somewhere on the server.

Part 1 : Designating the root of the server

Hint

Read about the Root Directory

Solution

From the reading material, we know that we can designate the root of the server with the character /. The command find / will allow us to search everywhere in the server.

Part 2 : Finding the relevant options

Hint

All the options we're looking for are in the section 2 of the gnu findutils documentation

Solution

Let's take a look at the section 2.8. In this section we can see the two options :

  • user
  • group

Thus we can deduce the resulting command : find / -user bandit7 -group bandit6 -size 33c. We just have to cat the resulting file to get the password.

Part 3 : Getting rid of all the error messages

Right now, you can see that the output is pretty useless, indeed we need to get rid of all the "Permission denied" messages.
We need to find a way to get rid of all these error messages, unfortunately find doesn't allow us to do so, but there is a way to get rid of these messages by putting them in a special file.

Hint

The information we need lies in two different places. Try to look into :

Solution

In the section 3.6.2 of the gnu bash manual, we can learn more about output redirection. I think this isn't written directly (but I may be wrong) in the documentation, but the find utility writtes its error messages to stderr(see here for a more precise documentation about the stderr file). However, we can redirect the output from stderr by redirecting the file descriptor number 2 to a file.
The file we're going to redirect to is the file /dev/null(we could also redirect to /dev/zero as writing to any of these file has the same effect).
Here is the full command find / -user bandit7 -group bandit6 -size 33c 2> /dev/zero. We can then run cat on the file we retrieved.

Full Solution

  1. find / -user bandit7 -group bandit6 -size 33c 2> /dev/zero to retrieve the only file that meets the requirements without printing all the error messages
  2. cat retrieved_file where retrieved_file is the file we got from the first step to dump the password string to stdout.

We could also use the one-liner : find / -user bandit7 -group bandit6 -size 33c -execdir cat '{}' \; 2> /dev/zero to dump only the password string to stdout

You can now jump to the next level

Bandit7->8

Level Goal

The password for the next level is stored in the file data.txt next to the word millionth.

Commands useful to solve the level

Helpful Reading Material

Where to start?

This Level is actually pretty easy and straightforward. We'll start by analysing the file and then we'll look for the command we need to complete our goal.

Part 1 : File Analysis

Let's first run the file utility on our data.txt file to know the type of content we have to deal with and to ensure that the file is not a malicious file that an attacker may have put in the directory to our intention.
Here is the output from the file command :

bandit7@bandit:~$ file data.txt 
data.txt: Unicode text, UTF-8 text
bandit7@bandit:~$

Now that we know that it is safe to run cat or any other utility that operates on text files on data.txt, let's try to run the head utility on this file.

Note : The head utility allows us to print only the few first lines (10 by default) of a file.

Here is the output from the head utility :

bandit7@bandit:~$ head data.txt
gallop	hu3ZhCrGRvfaO5jsY6ttvApzVCA2Hjvs
Aurelia's	ikl4F3cK5m6Cl6HAxva6zUAVJhI2Cvc6
stoicism	JiW9ts44udf20bJHe8H5dS1c99Muwz42
embodies	vWheZcAsQHZNnerI3ViW8wqOKIx0kbgR
Plato	dW2U8E5FfuAvNLdGDupP8GAxr922ZV0x
cultivation	A90E75jvWbEKrijFxM4GxqHEw8c8U2Bf
stable	omR4PHolFwbI0IEJsanveA21yWvFy8a7
bedspread	VlBFxuEDi3phEpljbKbahRJvJxfh3k9M
oppressing	hQTiEm5XF3cUQSEiBjh7sekemLOKBrcJ
darnedest	9O2zdCLKVoW5u34P9T7EKTZXcMRE6xh5
bandit7@bandit:~$ 

Now we can get a better feel of how our file is built. We can run a last test on the file in order to get an idea of how many data we'll have to search in order to retrieve the information we're looking for.
Let's try to run the wc utility on the file to count how many lines there are in it. We'll give it the -l option to ensure that only the line count gets printed.

Here is the output from the wc utility :

bandit7@bandit:~$ wc -l data.txt 
98567 data.txt
bandit7@bandit:~$

We now know that there are close to 100k lines in the data.txt file. This analysis is not always useful but it can give valuable insight. Let's now move on to the actual challenge, the password retrieval

Part 2 : Password retrieval

After learning more about the file structure, we can get a feel of which command will be useful to retrieve our password string. Let's take a look at the grep utility to know if we can use it to retrieve our password.

Hint

Looking into the grep man page, figure out how we can retrieve any line containing the word millionth in the file data.txt

Solution

By running the command grep millionth data.txt we can retrieve any line containing the word millionth in the file data.txt. The password string will be the second column of that file

Full Solution

  1. grep millionth data.txt to retrieve the only line containing the world millionth which also contains the password string, we can now copy and paste this password to go to the next level

Bonus : Cutting the output

Once we ran grep on our file, we get the following output :

bandit7@bandit:~$ grep millionth data.txt
millionth	password_string
bandit7@bandit:~$

We can now use the cut(1) utility to split the line we got into fields and only retrieve the field we need.

Hint

To know how to properly use the cut utility, look into the cut documentation page.

Solution

By running the line we got through a pipe and transferring its data to cut we can retrieve only the second field.
Here is the full command :

grep millionth data.txt | cut -f 2

You can now jump to the next level

Bandit8->9

Level Goal

The password for the next level is stored in the file data.txt and is the only line of text that occurs only once

Commands useful to solve the level

Helpful Reading Material

Where to start?

To get more information about the file analysis, go to the previous challenge.

We now need to get the only line of text that occurs only once in the file data.txt. To do so we'll do the process in reverse.

Part 1 : Uniquifying the file

The uniq utility allows us to discard repeated input lines, what it does is that it compares the consecutive lines to see if they're (or aren't) identical to each other.

Hint

Using the uniq gnu documentation, can you figure out what option we need to keep only the input lines that occurs only once?

Solution

The option -u is the option we'll need to complete our goal. Let's break down what it does. Here is a quote from the documentation :

The -u option : "Discard the last line that would be output for a repeated input group. When used by itself, this option causes uniq to print unique lines, and nothing else."
First let's try and see what would be the last line outputed for a repeated input group : it is the first line of the group. This means that uniq -u discards the only line which would be outputed for a group. This indeed means that uniq -u only prints the unique lines.

However we know from the same documentation that uniq operates on consecutive lines, thus we need a way to make the lines consecutive to compare them.

Part 2 : Sorting text files

We need a way to sort the input to apply our uniq filter to it.

Hint

By looking into the sort gnu documentation, can you figure out how to sort the input for the uniq filter to work?

Solution

It's fairly simple, we don't need any option at all. By running sort data.txt, we will output the result of the sorted output to stdout.

Full Solution

sort data.txt | uniq -u is the full command that will allow us to get the full password string.

  1. sort data.txt will allow us to sort the input in order for uniq to see the consecutive repeated lines.
  2. | the pipe will allow the sort and uniq processes to communicate.
  3. uniq -u will dump the only unique line to stdout.

You can now jump to the next level

Bandit9->10

Level Goal

The password for the next level is stored in the file data.txt in one of the few human-readable strings, preceded by several ‘=’ characters.

Commands useful to solve the level

Helpful Reading Material

Where to start?

As with the two previous levels, we are going to start by running file on our data.txt file.
Here is the output from this command :

bandit9@bandit:~$ file data.txt 
data.txt: data
bandit9@bandit:~$

We can now see that we are not dealing with a text file anymore, thus meaning that we can't use the utilities we're used to to retrieve our password.

Part 1 : Printing human-readable strings

To safely print human-readable strings in non text files, we can use the strings command. We need to figure out how to use this command to print the few human-readle strings in data.txt

Hint

Try to look in the strings(1) man page and find out how to use the strings utility to achieve this goal

Solution

We can use the --all option to ensure that all the file is scanned (this should be the default behavior but under certain implementations, the default behavior could be different so this we'll prevent the exploitation of any BFD library vulnerabilities).
Thus, the command we're looking for is strings --all data.txt, which will dump the human-readable strings to stdout.
Now, we just need to grep the character '=' in this output and look for the password string.

Full Solution

Here is the full command :

strings --all data.txt | grep =

We then need to look for the string that looks the most like what could be a password string, preceded by several '=' characters

Bonus : Using the size of the password string

We know that the password string is a 33 characters long string, so we know that the line we're looking for is at least 33 characters long. We are going to find a way to display only the (at least) 33 characters long strings in data.txt.

Hint

Reading the strings(1) man page, can you figure out an option to achieve this goal?

Solution

The option we're looking for is the -n option, we'll call it with -33 which means that we're looking for at least 33 characters long strings.

Here is the full command :

strings --all -33 data.txt

This command prints only one line, which contains the password we're looking for.

You can now jump to the next level

Bandit10->11

Level Goal

The password for the next level is stored in the file data.txt, which contains base64 encoded data.

Commands useful to solve the level

Helpful Reading Material

Where to start?

By running file on our file data.txt, we know that our file contains ASCII text we can now safely work with it. We know from the Level Goal that our file contains base64 encoded data. Let's first run head on our file
You should get an output close to this one :

VGhlIHBhc3N3b3JkIGlzIHBhc3N3b3JkX3N0cmluZwo=

As the solution is pretty straightforward I will give it right away, however it will still be hidden if you want to search by yourself first. Go and check the base64 gnu documentation page to see if you can find how to decode our password string.

Full Solution

We will use the option -d (decode) of the base64 utility.
Thus, running the following command :

base64 -d data.txt

will print a string which should look like this one : The password is password_string to stdout.

You can now jump to the next level

Bandit11->12

Level Goal

The password for the next level is stored in the file data.txt, where all lowercase (a-z) and uppercase (A-Z) letters have been rotated by 13 positions.

Commands useful to solve the level

Helpful Reading Material

Where to start?

By running file on our file data.txt, we know that our file contains ASCII text we can now safely work with it. We know from the Level Goal that our file contains rot13 encoded data. Let's first run head on our file
You should get an output close to this one :

Gur cnffjbeq vf cnffjbeq_fgevat

Part 1 : Describing our transformation

ROT13 is the rotation of all the alphabetic characters of half the alphabet (13 positions). The goal of this part is to describe the starting set and the ending set of letters as two strings. It will be useful for translating our rotated string back to its original form.

Hint

Using the information you got about ROT13 by reading the wikipedia page and assuming that the base set is the alphabet (first the Uppercase and then the lowercase letters), set that we will represent like this : A-Za-z which represents the string ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz. How would you represent the set of the translated characters by ROT13?

Solution

The set of the translated characters can be represented as N-ZA-Mn-za-m which is to be understood as the string NOPQRSTUVWXYZABCDEFGHIJKLMnopqrstuvwxyzabcdefghijklm

Part 2 : Translating our data back to its original form

Now that we described the starting and ending set of the ROT13 transformation, we need to know how to actually translate our data back to its original form. One thing we know from studying the rot13 behavior (and reading the wikipedia page) is that rot13 is a reciprocal cipher. Which means that rot13 applied to itself gives back the original message.
Let's take our two sets of strings and see if there is a tool that could help us do the translation.

Hint

By taking a look at the section 9 of the gnu coreutils documentation, try to see we can use one of the tool described there to achieve the desired outcome.

Solution

By reading the section 9.1.2 we can see that the tr utility is the right tool for us. By using it with the two sets of character we deduced in the previous part and by redirecting the input from our data.txt file, we can achieve the desired outcome.
Here is the full command :

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

Which will output something along the lines : The password is password_string

Full Solution

  1. tr 'A-Za-z' 'N-ZA-Mn-za-m' < data.txt is the command we use to translate our string back to its original form.

You can now jump to the next level

Bandit12->13

Level Goal

The password for the next level is stored in the file data.txt, which is a hexdump of a file that has been repeatedly compressed. For this level it may be useful to create a directory under /tmp in which you can work. Use mkdir with a hard to guess directory name. Or better, use the command “mktemp -d”. Then copy the datafile using cp, and rename it using mv (read the manpages!)

Commands useful to solve the level

Helpful Reading Material

Where to start?

To prepare for our chase, we will follow the instructions and place ourselves in a temporary directory created for the occasion. Then, we will repeatedly uncompress our data using different compression utilities until we get the password string.

Part 1 : Preparing for the extraction

To do so, we will use the mktemp utility, cd into it and cp our data.txt file to our newly created directory.

Hint

Using the 3 links to documentation pages, can you figure out how to move to a temporary directory and copy the data.txt file to it?

Solution

We will run the following command :

cd "$(mktemp -d)" && cp "$HOME"/data.txt .

This will move us to a temporary created directory and copy the file that lies at '/home/bandit12/data.txt' to the directory we're in.
We are now ready to work with this file.

Part 2 : Getting the binary file

Now that we're in a temporary directory (which we had to move to because the user bandit12 can't write to their home directory, we'll come back to file permissions in the later challenges), we can start working with our file. The only information we have about this file is that its the hexdump of a file that has been repeatedly compressed.
Running file on this file doesn't give us much more as it only tells us that the file we're seeing is a text file. We need a utility that can translate back the hexdump of a file to its original form.

Hint

Looking at the hexdump and the xxd man pages, can you figure out a way to revert data.txt back to its original state?

Solution

The command we're going to use is the xxd command. To use it properly and get the original form of the data.txt file, we're going to specify the outfile we want to write to and specify xxd that we want it to operate in reverse mode.
Here is the final command :

xxd -r data.txt outfile

Where outfile may be any name you want to give to your retrieved data.

Part 3 : Figuring out the procedure we're going to follow to extract all the data

Now that we have our binary data, I won't go step by step into the solution because even though there are quite a few steps, they ultimately can be resolved to a sequence of 3 actions. Our goal is to figure out what are these 3 actions.

Hint

By trying to extract our outfile a first time, can you figure out what the sequence of actions is? You might need to use the file utility to achieve that goal.

Solution

Here is the sequence of actions we need to follow to successfully extract all the data that has been compressed :

  1. We need to find the compression method of our file by running the file utility on it.
  2. Then, we might need to rename the file we're looking to uncompress to a file with the right extension (as some compression utilities recognize only some specific extensions)
  3. We need to extract the file with the right utility and go back to step 1 until we get an ASCII Text file.

If you're stuck at this time, go to the full solution to get the step by step walkthrough.

Full Solution

Running the following sequence of commands :

cd "$(mktemp -d)" && cp "$HOME"/data.txt . || kill -INT $$
xxd -r data.txt outfile
file --mime-type -b outfile # should print : application/gzip
mv outfile outfile.gz
gunzip outfile.gz
file --mime-type -b outfile # should print : application/x-bzip2
bunzip2 outfile
file --mime-type -b outfile.out # should print : application/gzip
mv outfile.out outfile.gz
gunzip outfile.gz
file --mime-type -b outfile # should print : application/x-tar
tar -xf outfile
file --mime-type -b data5.bin # should print : application/x-tar
tar -xf data5.bin
file --mime-type -b data6.bin # should print : application/x-bzip2
bunzip2 data6.bin
file --mime-type -b data6.bin.out # should print : application/x-tar
tar -xf data6.bin.out
file --mime-type -b data8.bin # should print : application/gzip
mv data8.bin data8.gz
gunzip data8.gz

should fully uncompress the file data.txt. We can now run one last time the file command on our file data8.

file --mime-type -b data8 # should print : text/plain

This shows us that the data8 file contains the password we're looking for, and by running cat on this file, we get something along the lines :

The password is password_string

Bonus : One-Liner Solution

Instead of extracting all these files to another file and renaming that file, we could each time pipe the output of our decompression program to the file utility. That way it would be possible to analyse the output of the program without creating a new file each time. It is very unoptimized for huge files (as you don't know in advance what was the sequence of compressions applied thus meaning you'll have to uncompress the same file a lot of times) but it is worth mentionning as once you have this sequence, it can be very useful to have one command rather than a shell script.

Hint

Using only the man pages of the commands we used in the previous section, can you figure out a way to write a pipeline that does the exact same thing without creating any file?

Solution

The command we're looking for is the following :

xxd -r data.txt | gunzip -c | bunzip2 -c | gunzip -c | tar --to-command='/usr/bin/tar -xO' -x | bunzip2 -c | tar -xO | gunzip -c

You can understand all the options that have been added by reading the man pages of all the utilities involved.

You can now jump to the next level

Bandit13->14

Level Goal

Commands useful to solve the level

Helpful Reading Material

Where to start?

First, we need to know what is in our directory. Here is the output from the ls command :

bandit13@bandit:~$ ls
sshkey.private
bandit13@bandit:~$

from now on we can already use that ssh key to connect to bandit14 user. However, we'll first retrieve the ssh key on our machine so that we can log into the user bandit14 without the need to be logged in as the user bandit13.

Part 1 : Retrieval of the ssh key

To retrieve the ssh key using the ssh protocol, we're going to need the command that stands for secure copy, the scp command.

Hint

Reading the scp man page, can you figure out a way to retrieve the ssh key from the bandit13 user on the overthewire server?

Solution

We'll have to run the command while not connected to the remote server, as the scp protocol will connect to the remote server and retrieve the file for us.
From the scp man page, we know the following : "The source and target may be specified as a local pathname, a remote host with optional path in the form [user@]host:[path], or a URI in the form scp://[user@]host[:port][/path]. Local file names can be made explicit using absolute or relative pathnames to avoid scp treating file names containing ‘:’ as host specifiers.". One precision to add is that the path argument is starting from the user's home directory.

Thus we can deduce the structure of the call we have to make :

  • For the source, we will specify the URI as follows : scp://bandit13@bandit.labs.overthewire.org:2220/sshkey.private
  • For the target, we will specify the local pathname we want to store the file in, let's say : ./bandit14_sshkey

Thus, the command we're looking for is : scp scp://bandit13@bandit.labs.overthewire.org:2220/home/bandit13/sshkey.private ./bandit14_sshkey

Part 2 : Setting the right file permissions

Now that we have a private ssh key, we need to use it to connect other ssh. To do so, as it is a private ssh key, it must meet some requirements on the file permissions. Our goal is to set the right file permissions for us to be allowed to connect to the user bandit14.

Hint

By looking into the FILES section of the ssh man page and the chmod gnu documentation page, can you figure out the right file permissions for the private key and set them accordingly?

Solution

In the portion describing the file ~/.ssh/id_rsa, we can read that this file should be readable by the user and should not be accessible by others.
Running the stat utility on the file gives us the file permissions of our ssh private key. Here is the output from this command :

  File: bandit14_sshkey
  Size: 1679      	Blocks: 8          IO Block: 4096   regular file
Device: 804h/2052d	Inode: 8913955     Links: 1
Access: (0640/-rw-r-----)  Uid: ( 1001/ Charystag)   Gid: ( 1001/ Charystag)
Access: 2024-06-03 21:05:42.285372019 +0200
Modify: 2024-06-03 21:05:11.765802230 +0200
Change: 2024-06-03 21:05:11.765802230 +0200
 Birth: 2024-06-03 21:05:11.733802682 +0200

We can now see, (helping ourselves from the documentation about file permissions) that this file is readable and writable by the user and readable by the other members of the user's group. As we don't need to write data to the private key file, we can restrict the permissions to the minimum, we'll only allow the current user (us) to write to the file.
The following call to the chmod utility will allow us to achieve our goal : chmod 400 bandit14_sshkey.

Part 3 : Connecting using the ssh key

Now that the right file permissions are set, the last thing we need to do is to connect to the user bandit14 using our private ssh key.

Hint

Going back into the ssh(1) man page, can you figure out an option that would allow us to use the ssh key we just got to connect to the user bandit14 ?

Solution

The option we're looking for is the option -i which allows us to use our identity_file to connect without the need for a password. This is our full command : ssh -p 2220 -l bandit14 -i bandit14_sshkey bandit.labs.overthewire.org

Full Solution

  1. scp scp://bandit13@bandit.labs.overthewire.org:2220/home/bandit13/sshkey.private ./bandit14_sshkey to retrieve the private ssh key from the bandit13 user
  2. chmod 400 bandit14_sshkey to set the right file permissions and allow us to connect over ssh
  3. ssh -i bandit14_sshkey ssh://bandit14@bandit.labs.overthewire.org:2220 to finally connect to user bandit14

You can now jump to the next level

Bandit14->15

Level Goal

The password for the next level can be retrieved by submitting the password of the current level to port 30000 on localhost.

Commands useful to solve the level

Helpful Reading Material

Where to start?

We have to start by reading a lot of documentation, to understand a bit more what we have to do and what we're talking about. First things first, before even trying to send the password, we need to retrieve it first.

Part 1 : Password retrieval

We need to retrieve the password by finding in which file it is stored, the information for the password file is actually displayed at the beginning of each level, when we connect to the user we're accessing over ssh.

Hint

By using the prompt we get when logging in to bandit14 (or any other overthewire user), can you figure out where is the password for bandit14 stored?

Solution

Let's start by recalling the instructions for each level :


  This machine might hold several wargames.
  If you are playing "somegame", then:

    * USERNAMES are somegame0, somegame1, ...
    * Most LEVELS are stored in /somegame/.
    * PASSWORDS for each level are stored in /etc/somegame_pass/.

This tells us that the password we're looking for is in the file /etc/bandit_pass/bandit14. Let's run a quick stat on this file to ensure we can read it. Here is the output from this command :

  File: /etc/bandit_pass/bandit14
  Size: 33        	Blocks: 8          IO Block: 4096   regular file
Device: 10301h/66305d	Inode: 517564      Links: 1
Access: (0400/-r--------)  Uid: (11014/bandit14)   Gid: (11014/bandit14)
Access: 2024-06-03 22:30:53.614318247 +0000
Modify: 2023-10-05 06:19:04.167222286 +0000
Change: 2023-10-05 06:19:04.167222286 +0000
 Birth: 2023-10-05 06:19:04.167222286 +0000

As we're logged in as user bandit14, we know we can access this file which contains the 33 bytes password string we need to complete this level.

Part 2 : Port scanning

We know that there is a service listening on port 30000, let's start by scanning the port 30000 to get a better idea of how to communicate with the service listening on port 30000.

Hint

Using only the description section of the nmap man page, can you figure out how to scan the localhost network in order to see which ports are in use?

Solution

The command we're looking for is nmap localhost, which will allow us to scan the network at 127.0.0.1. Here is the output from this command :

bandit14@bandit:~$ nmap localhost
Starting Nmap 7.80 ( https://nmap.org ) at 2024-06-04 10:17 UTC
Nmap scan report for localhost (127.0.0.1)
Host is up (0.00022s latency).
Not shown: 993 closed ports
PORT      STATE SERVICE
22/tcp    open  ssh
1111/tcp  open  lmsocialserver
1122/tcp  open  availant-mgr
1840/tcp  open  netopia-vo2
4321/tcp  open  rwhois
8000/tcp  open  http-alt
30000/tcp open  ndmps

Nmap done: 1 IP address (1 host up) scanned in 0.11 seconds
bandit14@bandit:~$

We can see that the port 30000 is open and accepts tcp connections. This tells us that we need to send the password using the tcp protocol.

Part 3 : Password sending

Now that we know that we need to send the password using the tcp protocol, we need a tool that is able to do that for us. Here is when the nc tool comes in handy.

Hint

By using the TALKING TO SERVERS section of the nc(1) man page, can you figure out how to send the password to the server?

Solution

nc will have to read the password from stdin to send it to the server, there are a few ways to do so but one command you could run is the following :

nc localhost 30000 < /etc/bandit_pass/bandit14

which redirects stdin from the file containing the pasword for user bandit 14. Once this is done, you should see the following output :

bandit14@bandit:~$ nc localhost 30000 < /etc/bandit_pass/bandit14
Correct!
password_string

bandit14@bandit:~$

where pasword_string is our 33 bytes characters password string.

Full Solution

  1. cat /etc/bandit_pass/bandit14 | nc localhost 30000 to send the password to the service listening at localhost:30000.

You can now jump to the next level

Bandit15->16

Level Goal

The password for the next level can be retrieved by submitting the password of the current level to port 30001 on localhost using SSL encryption.

Helpful note: Getting “HEARTBEATING” and “Read R BLOCK”? Use -ign_eof and read the “CONNECTED COMMANDS” section in the manpage. Next to ‘R’ and ‘Q’, the ‘B’ command also works in this version of that command…

Commands useful to solve the level

Helpful Reading Material

Where to start?

For the network analysis and password retrieval, you can go to the previous challenge. In this challenge I'm only going to show how to use the s_client command from the openssl program to efficiently communicate with our server.

Part 1 : Communicating with the SSL server

Our first goal is to lean how to open a connection with our SSL server, to do so we are going to use the s_client command.

Hint

By looking at the s_client man page and only looking for the fields that talk about connecting to the SSL server, can you figure out a way to open a connection with the server? The server will read all its input from stdin

Solution

By running the command openssl s_client localhost:30001 or openssl s_client -connect localhost:30001, you can open a connection with the server.

Part 2 : Sending the password to the server using the client

Now that we've opened a connection to the server, we want to send the password. We could copy and paste the password, press enter and then ^C the client and it would actually work but this is not what we are going to do here.

As the password is contained within the file /etc/bandit_pass/bandit15, it would be way easier to just redirect the input from that file. However, when we do it like this, no password appears on the standard output. Our goal is to fix that issue.

Hint

By looking at the CONNECTED COMMANDS section of the s_client man page, try to understand why we can observe such a behavior and then, look at the OPTIONS section to see if you can retrieve an option that will fix this behavior.

Solution

We can observe such behavior because at the end of any file, there is an EOF character that is interpreted by our s_client command as a signal to close the connection. By using the option -ign_eof we can explicitely tell s_client to keep the connection open, and thus receive the password from the server.
Here is our final command :

openssl s_client -ign_eof localhost:30001 < /etc/bandit_pass/bandit15

Full Solution

  1. openssl s_client -quiet localhost:30001 < /etc/bandit_pass/bandit15 to retrieve the password from the SSL server

You can now jump to the next level

Bandit16->17

Level Goal

The credentials for the next level can be retrieved by submitting the password of the current level to a port on localhost in the range 31000 to 32000. First find out which of these ports have a server listening on them. Then find out which of those speak SSL and which don’t. There is only 1 server that will give the next credentials, the others will simply send back to you whatever you send to it.

Commands useful to solve the level

Helpful Reading Material

Where to start?

What we need to do here is to find a way to scan the network so we know on which port the server we need to talk to resides. First, let's scan the network between the port 31000 and 32000. To do so, we can use two utilities, nmap and nc

Part 1 : Basic Port Scanning with nc

Hint

By lookint at the PORT SCANNING section of the nc man page, can you figure out a way to perform a basic scan of the ports between 31000 and 32000 with nc?

Solution

With nc, we can use the following command :

nc -zv localhost 31000-32000 |& grep -v -E '^nc'

Let's break down how it works :

  1. nc -zv localhost 31000-32000 tells nc to report the open ports between the port 31000 and 32000, writing verbose output to stderr
  2. |& is a metacharacter that is equivalent to 2 >& 1 | which means to redirect stdout and stderr through a pipe (see pipelines in the gnu bash manual for more information)
  3. grep -v -E '^nc' uses the regular expression ^nc to mach lines beginning by 'nc' and the -v option uses the inverted match to match only the lines that don't begin with nc (meaning the only lines that didn't report an error).

Here is the output from this command :

bandit16@bandit:~$ nc -zv localhost 31000-32000 |& grep -v -E '^nc'
Connection to localhost (127.0.0.1) 31046 port [tcp/*] succeeded!
Connection to localhost (127.0.0.1) 31518 port [tcp/*] succeeded!
Connection to localhost (127.0.0.1) 31691 port [tcp/*] succeeded!
Connection to localhost (127.0.0.1) 31790 port [tcp/*] succeeded!
Connection to localhost (127.0.0.1) 31960 port [tcp/*] succeeded!
bandit16@bandit:~$

Part 2 : Basic Port Scanning with nmap

Hint

By using the PORT SPECIFICATION AND SCAN ORDER section of the nmap man page, can you figure out a way to perform a basic scan of the ports between 31000 and 32000 with nmap?

Solution

With nmap, it is even more simple. We just need to provide the range of ports to scan as nmap is already a port scanner.

Here is the command we're looking for :

nmap localhost -p 31000-32000

Here is the output from this command :

Starting Nmap 7.80 ( https://nmap.org ) at 2024-06-04 14:27 UTC
Nmap scan report for localhost (127.0.0.1)
Host is up (0.00013s latency).
Not shown: 996 closed ports
PORT      STATE SERVICE
31046/tcp open  unknown
31518/tcp open  unknown
31691/tcp open  unknown
31790/tcp open  unknown
31960/tcp open  unknown

Nmap done: 1 IP address (1 host up) scanned in 0.05 seconds

Part 3 : Service detection with nmap

Now that we have a little more info about the open ports, we can now run a more advance scan using nmap on the open ports we found.

Hint

By taking a look at the SERVICE AND VERSION DETECTION section of the nmap man page, can you figure out how to know on which port resides the service we want to communicate with ?

Solution

The -sV option is the option we're looking for, it will allow us to identify the service that lies on each port that we're scanning. As the scan doesn't need to be full (as 4 out of 5 of these services will echo back to the sender all the information they receive), we will enable the option --version-light so that the scan takes less time.

The command we're looking for is the following :

nmap -sV --version-light -p 31046,31518,31691,31790,31960 localhost

We could of course, also run this command on the whole set of ports between the range 31000 and 32000 with nmap -sV --version-light -p 31000-32000.

Here is the output from this command (--version-light is an alias for --version-intensity 2):

bandit16@bandit:~$ nmap -sV --version-intensity 2 localhost -p 31046,31518,31691,31790,31960
Starting Nmap 7.80 ( https://nmap.org ) at 2024-06-04 14:38 UTC
Nmap scan report for localhost (127.0.0.1)
Host is up (0.00013s latency).

PORT      STATE SERVICE     VERSION
31046/tcp open  echo
31518/tcp open  ssl/echo
31691/tcp open  echo
31790/tcp open  ssl/unknown
31960/tcp open  echo
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port31790-TCP:V=7.80%T=SSL%I=2%D=6/4%Time=665F2708%P=x86_64-pc-linux-gn
SF:u%r(GenericLines,31,"Wrong!\x20Please\x20enter\x20the\x20correct\x20cur
SF:rent\x20password\n")%r(GetRequest,31,"Wrong!\x20Please\x20enter\x20the\
SF:x20correct\x20current\x20password\n")%r(SSLSessionReq,31,"Wrong!\x20Ple
SF:ase\x20enter\x20the\x20correct\x20current\x20password\n")%r(TLSSessionR
SF:eq,31,"Wrong!\x20Please\x20enter\x20the\x20correct\x20current\x20passwo
SF:rd\n");

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 32.61 seconds
bandit16@bandit:~$

We know now that we can use the s_client command to send the password to the server listening at 31790 and retrieve the ssh key to connect to bandit 17.

Full Solution

  1. nmap -sV --version-light -p 31000-32000 to retrieve the server that is listening for our password
  2. openssl s_client -ign_eof localhost:31790 < /etc/bandit_pass/bandit16 to retrieve the private ssh key needed to connect to bandit 17.

Bonus : Writting the ssh key directly to a file

Wouldn't it be way more suitable to output our ssh key directly to a file? Fortunately, there is an easy way to do so.

Hint

Searching again into the s_client man page, can you figure out a way to output the ssh_key directly to a file ?

Solution

First, we need to create a file to store the private ssh key, we'll create it using the mktemp utility. Then, using the -sess_out option we will be able to output our ssl session (which is the ssh key) directly to the file.

This is what our set of commands look like :

PRIVATE_KEY="$(mktemp)"
openssl s_client -ign_eof -sess_out "$PRIVATE_KEY" localhost:31790 < /etc/bandit_pass/bandit16`

Then we can run echo "$PRIVATE_KEY" to get the name of the file and use the scp command to retrieve the file on our machine.

You can now jump to the next level

Bandit17->18

Level Goal

There are 2 files in the homedirectory: passwords.old and passwords.new. The password for the next level is in passwords.new and is the only line that has been changed between passwords.old and passwords.new.

NOTE: if you have solved this level and see ‘Byebye!’ when trying to log into bandit18, this is related to the next level, bandit19.

Commands useful to solve the level

Helpful Reading Material

Where to start?

We know that there are two files in our directory that we can view with the ls command : passwords.old and passwords.new, we are going to need to output the difference between those two files. We know that the new password is the line that has been updated in the passwords.new file.

Part 1 : Output the diff of the files

We want to see the differences between both files printed to standard output.

Hint

By looking at the diff man page, can you figure out how to use the diff(1) utility to output the differences between these two files?

Solution

To do so, we simply need to run the following command :

diff passwords.old passwords.new

The lines that are different in passwords.old (the first argument) will appear prefixed by the symbol < while the lines different in passwords.new will appear prefixed by a >. The password is the line that is different in passwords.new

Part 2 : Making diff output a bit more readable

Right now, we need to remember that the different lines from the first file will be prefixed by < and the ones from the second file by >. Let's find a way to make the output more readable so that we don't need to remember this information.

Hint

Looking more deeply into the diff man page, can you figure out a way to make the output more readable ?

They are a lot more than one answer.

Solution

For example, one could use the option -u to print a string at the beginning of the diff output which will look like the one below :

--- passwords.old	2023-10-05 06:19:27.827277353 +0000
+++ passwords.new	2023-10-05 06:19:27.835277371 +0000

which means that all the lines from passwords.old will be prefixed by - and the lines from passwords.new will be prefixed by +. The diff -u output will be the following :

--- passwords.old	2023-10-05 06:19:27.827277353 +0000
+++ passwords.new	2023-10-05 06:19:27.835277371 +0000
@@ -39,7 +39,7 @@
 WFB9ezoSnb146RUbbX6d9Yx2sU46Q8Ax
 JFkUvvpfLmE7KBkAEePwZndBr33oFzh8
 wDn38KGxWKk7dp39odF7fWLT6aljqEsK
-old_password
+new_password
 RKMlN2JZydt4j5rQjJt07GgNqtgnq8dw
 nssByafsRMwebfyRhMWKSqX39xF1l4Hr
 ZlUzDUTd4faumV8wCtJ4CHA788tySKDO

We can then use the new password to go to the following level.

Full Solution

  1. diff passwords.old passwords.new to output the differences between the two files and retrieve the new password from the passwords.new file.

You can now jump to the next level

Bandit18->19

Level Goal

The password for the next level is stored in a file readme in the homedirectory. Unfortunately, someone has modified .bashrc to log you out when you log in with SSH.

Commands useful to solve the level

Helpful Reading Material

Where to start?

We know that someone has modified our .bashrc file in order to log us out when we log in using SSH. The .bashrc file is a file where commands are read from when bash is invoked as an interactive shell that is not a login shell (see bash invocation for more informations). What we are going to do is to find out how to retrieve the readme file from bandit18 home directory.

Part 1 : Running a command on the remote host

As we are anyway login in using the ssh protocol, we need to find a way to run a command non-interactively on the remote host.

Hint

Once again, look into the ssh man page, find a way to execute a command on the remote host instead of an interactive shell.

Solution

To do so, we just need to append the command we want to run at the end of our ssh command, it will then be run instead of an interactive shell when we log in into the user bandit18. Our ssh command is the following :

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

Because we need to cat the readme file in bandit18 home directory. It will print the password string to sdout and exit.

Full Solution

  1. ssh ssh://bandit18@bandit.labs.overthewire.org:2220 cat readme to cat the readme file in bandit18 home directory.

You can now jump to the next level

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

Commands useful to solve the level

Helpful Reading Material

Where to start?

The only thing we have in our directory is an executable called bandit20-do, the instructions for this level tell us that we should execute it without arguments to find out how to use it.

Part 1 : Using the setuid binary

When running the executable without arguments, we see the following :

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

We need to find out how to use this executable to print the password for the next level on stdout.

Hint

Using the example of the bandit20-do executable, can you figure out the command to execute to print the bandit20 password to stdout?

Solution

When running the example, we can see the following output :

bandit19@bandit:~$ ./bandit20-do id
uid=11019(bandit19) gid=11019(bandit19) euid=11020(bandit20) groups=11019(bandit19)
bandit19@bandit:~$

We can see that our effective user id (euid) is bandit20 when we run this executable, which means that we can do everything that the user bandit20 can do.

By running the stat command on the file /etc/bandit_pass/bandit20 we see the following output :

  File: /etc/bandit_pass/bandit20
  Size: 33        	Blocks: 8          IO Block: 4096   regular file
Device: 10301h/66305d	Inode: 517599      Links: 1
Access: (0400/-r--------)  Uid: (11020/bandit20)   Gid: (11020/bandit20)
Access: 2024-06-04 21:38:04.747961484 +0000
Modify: 2023-10-05 06:19:06.591227890 +0000
Change: 2023-10-05 06:19:06.595227900 +0000
 Birth: 2023-10-05 06:19:06.591227890 +0000

This tells us that the file is only readable by the user bandit20, however thanks to the bandit20-do executable, we are the user bandit20. We can thus cat this file and retrieve the password string.
Here is the final command :

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

Full Solution

  1. ./bandit20-do cat /etc/bandit_pass/bandit20 to print the password string on stdout.

You can now jump to the next level

Bandit20->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

Commands useful to solve the level

Helpful Reading Material

Where to start?

We can start this level by running the executable su_connect without any argument to try and get a better feel of what we should do with this program.

Here is the output from this command :

Usage: ./suconnect <portnumber>
This program will connect to the given port on localhost using TCP. If it receives the correct password from the other side, the next password is transmitted back.

What we can deduce at this moment is that the program suconnect is a TCP client, this means that we need to set up a server in order for our suconnect client to communicate with.

Part 1 : Setting up a TCP server that sends the password

The first part of this challenge is to set up a TCP server that we will use to send the password to any host that would be trying to connect.

Hint

Using the CLIENT/SERVER MODEL of the nc command, can you figure out a way to set up a server that will listen for incoming connections and send the password to any client that listens to it?

Solution

Let's copy the exact same code from the nc man page example. Here is the command we're going to use :

nc -l 1234

We can now notice that we have an open TCP server that listens for incoming connections. However our server is pretty basic (as it does absolutely nothing), however it can communicate through its standard input and output. Let's try to redirect the standard input of the server from a file.

By running the following command :

nc -l 1234 < /etc/bandit_pass/bandit20

We have a server listening on the port 1234 that will send the password for bandit20 to any host that tries to connect ot it.

Part 2 : Running the TCP server as a background process

After running our command, we can notice that the TCP server is waiting and that we won't have any access to our terminal while the server is still running. Our goal here is to find a way to keep the server open and to communicate with it using the same terminal session.

Hint

By using the Helpful Reading Material, can you figure out a way to run our server as a background process so that you can continue communicating with the server using your current terminal session?

Solution

To continue communicating with the server using the current terminal session, we need to launch it asynchronously, this means that the exit status of our command will be 0 and that bash won't wait for the completion of your command to give us back the control of our terminal session.

Here is the command we're going to execute.

nc -l 1234 < /etc/bandit_pass/bandit20 &

It will tell bash to run this process as a background process, which will allow us to run our TCP client to retrieve the password for the next level.

Part 3 : Communicating with our server

Now that we have a server running and listening on the port 1234, we can use our executable to communicate with it. This part is pretty straightforward so there won't be any Hint.

Solution

We just need to run the executable suconnect and to specify it the right port number.

Here is the output from that command :

bandit20@bandit:~$ ./suconnect 1234
Read: bandit20_pass
Password matches, sending next password
bandit21_pass
[1]+  Done                    nc -l 1234 < /etc/bandit_pass/bandit20
bandit20@bandit:~$

You can see that on the last line, bash reports that the our server in the background has returned as it closes after receiving one connection.

Full Solution

  1. nc -l port_number < /etc/bandit_pass/bandit20 & to run a server listing on port_number in the background.
  2. ./suconnect port_number to retrieve the password for the next level.

You can now jump to the next level

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

Commands useful to solve the level

Helpful Reading Material

Where to start?

We are now starting a series of levels based on cron, which is a job scheduler on Linux. For this series, we are going to need to analyse the cron jobs for the users involved in this series of level and see what we informations we can gather from this analysis. Let's start with the level21.

Part 1 : Retrieving the cronjob for the user bandit22

Our goal here is to know which script is executed by the cronjob on the session of user bandit22.

Hint

By analysing the files into the /etc/cron.d directory, can you retrieve the contents of the script that runs for the bandit22 user?

Solution

Here is the output from the ls command for the /etc/crond.d directory :

bandit21@bandit:~$ ls /etc/cron.d
cronjob_bandit15_root  cronjob_bandit17_root  cronjob_bandit22  cronjob_bandit23  cronjob_bandit24  cronjob_bandit25_root  e2scrub_all  otw-tmp-dir  sysstat
bandit21@bandit:~$

We see that there is a file named cronjob_bandit22 in the directory. Let's cat the contents of this file :

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
bandit21@bandit:~$

This shows us that there is a cronjob running every minute for the user bandit22. We are going to go on and print the contents of the script :

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

This finally tells us that the password for the user bandit22 is stored in the temporary file which name is written in the script and that is readable by everyone.

We can go on and finally retrieve this password to jump to the next level.

Full Solution

  1. cat /etc/cron.d/cronjob_bandit22 to know which cron job is executed for the user bandit22.
  2. cat /usr/bin/cronjob_bandit22.sh to view the contents of the script that the cron job for user bandit22 runs
  3. cat tmpfile to print the password string on stdout.

You can now jump to the next level

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

Commands useful to solve the level

Helpful Reading Material

Where to start?

To know how to retrieve the script for this level, you can refer to the previous level. We will focus only on the script analysis for this challenge

Part 1 : Script analysis and password retrieval

When we retrieve the script from the file /usr/bin/cronjob_bandit23.sh, we can see that it defines two variables and that it uses them to print the password for the next level into a file in the /tmp directory.

Hint

Using the useful commands, can you figure out what the script does and in which file it prints the password for the next level?

Solution

Let's analyse this script.

  • The myname variable hold the output of the command whoami, which prints the username associated with the current user id. As this script is ran by bandit23, we know that the value of the myname variable is bandit23.
  • The mytarget variable holds the output of the following pipeline (where myname has been replaced by its value) : echo I am user bandit23 | md5sum | cut -d ' ' -f 1.

The md5sum utility will hash the phrase it receives on standard input and output something following this format :

hashed_phrase filename

with hashed_phrase and filename separated by a space (here filename is - because the file is standard input). Finally, the cut utility will retrieve only the first field (by splitting the fields using the space character), which is the hash of our phrase.

As standard input and standard error are redirected to /dev/null, we won't see the output from the echo command, however we can notice that the password for bandit23 is printed in the file /tmp/hashed_phrase.

Thus, we know that by running the following command :

cat /etc/8ca319486bfbbc3663ea0fbe81326349

we can retrieve the password for the bandit23 user.

Full Solution

  1. cat /etc/8ca319486bfbbc3663ea0fbe81326349 to retrieve the password for the next level

You can now jump to the next level

Bandit23->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…

Commands useful to solve the level

Helpful Reading Material

Where to start?

The goal of this level, is to have you write your first shell script (if you didn't write one before). Even though the shell scripts we're going to write in this level are pretty simple, I think it is a very good occasion to learn more about File Permissions so that after beating this level you'll understand deeply what they mean and how to set them properly.

On a more personal note, even though I did a lot of bash scripting before, and thus thought that this level would be a piece of cake, it took me 3 days to figure out the solution to that level. Not because I couldn't write a script but because I never cared that much about file permissions so you can trust me, I'll set you on the right track for thinking about file permissions in a Linux environment.

For the script retrieval, as with the previous level, I'll let you see the level 21 and then come back when the script is in front of your eyes.

Part 1 : Script analysis

In this first part, we're going to analyse the script in order to know what it is about and how to use it to retrieve the password for the next level.

Hint

Using the useful commands and the helpful reading material, can you figure out what the script does?

Solution

We already know from the previous level that the script changes the directory to /var/spool/bandit24/foo as bandit24 is the value contained in the myname variable.

The script then executes as follows :

  1. for all the files and the hidden files in the directory, it executes a loop (see Filename Expansion for more explanations about how the patterns are matched).
  2. In this loop, if the filename is not '.' or '..', then it does the test that follows.
  3. The call to the stat command only prints the username of the owner of the file. So if the owner is bandit23 (which is us), it runs the following command.
  4. The call to the timeout utility runs the script for at most 60 seconds and then sends a SIGKILL signal to the script to ensure it stops.
  5. Regardless of whether the script was executed or not, the script is removed, which means that the directory ends up being totally empty at the end of the cronjob execution.

Part 2 : Creating a basic script

Now that we know what the program does, we understand that we just need to create a script and put it in the right folder (which is /var/spool/bandit24/foo) and if properly written it will give us the password for the bandit24 level.

Hint

Using the previous level, can you design a simple script to print bandit24 password in a way we can retrieve it? Recall that all output is redirected to /dev/null, which means you may have to create a file where bandit24 will be able to write the password to.

Solution

For this basic script, we'll copy the model of bandit22. This means that we'll write a simple script that prints the password to a custom file. Here is the script we'll use :

#!/usr/bin/env bash

filename="$(echo Hello my fellow mates | md5sum | cut -d ' ' -f 1)"
cat /etc/bandit_pass/bandit24 > /tmp/${filename}

We're going to store this script in a file called script (but you can use any name you want) and copy this file to the right location, which is /var/spool/bandit24/foo. We will then run a for loop to check for the script presence in this folder (in order to know whether or not the script has executed and to see if it has done what we want)

  1. First, we're going to go in a temporary directory (with cd "$(mktemp -d /tmp/hello_fellow_mates.XXXXXXXXXX)") Don't worry about the funny name template, I just though it was funnier that just using tmp everytime
  2. Then we are going to write our script to a file called script
  3. Then we are going to run the following commands :
cp script /var/spool/bandit24/foo/script
echo -n Waiting for cronjob
while stat /var/spool/bandit24/foo/script >& /dev/null ; do echo -n . ; sleep 1 ; done 
echo -e '\n'cronjob executed

The last part allows us to monitor (using the stat command that returns true if it found the script at the specified location) the script and see when it is executed (thus meaning that we should expect an output).

Part 3 : Setting the right file permissions

Once the cronjob has executed, when we try to run cat /tmp/"$(echo Hello my fellow mates | md5sum | cut -d ' ' -f 1)", we see the following output :

bandit23@bandit:/tmp/hello_fellow_mates.1JNCwI8su9$ cat /tmp/$(echo Hello my fellow mates | md5sum | cut -d ' ' -f 1)
cat: /tmp/af1eebe9db8a5242b192026716ddde8f: No such file or directory
bandit23@bandit:/tmp/hello_fellow_mates.1JNCwI8su9$

Wtf Chary, you told us that this script was working??

Well, bear with me because it is. The only thing we need is to give bandit24 the permission to execute it. Until now, it was not executing because bandit24 wasn't granted the rights to run the script, thus deleting it without even running it.

Hint

By reading the File Permissions section of the gnu coreutils documentation, can you figure out how to set the right file permissions for the script to actually execute?

Solution

Lets run a quick stat on our script file. We will run the following command :

stat -c '%A Uid: (%u/%U)  Gid: (%g/%G)' script

to print only the relevent information. Here is the output from that command :

-rw-rw-r-- Uid: (11023/bandit23)  Gid: (11023/bandit23)

This tells us no one is allowed to execute the script. As bandit24 is not in the group bandit 23 (see the group man page for more informations about it). We can deduce that bandit24 belongs in the others category.

By running the following command :

chmod o+x script

We can allow the other users to execute the script, thus allowing bandit24 to execute the script. We can then run our precedent set of commands :

cp script /var/spool/bandit24/foo/script
echo -n Waiting for cronjob
while stat /var/spool/bandit24/foo/script >& /dev/null ; do echo -n . ; sleep 1 ; done 
echo -e '\n'cronjob executed

And then, when we run cat /tmp/"$(echo Hello my fellow mates | md5sum | cut -d ' ' -f 1)", we can see the password printed to stdout.

Full Solution

  1. cd "$(mktemp -d)" to change directory to a temporary directory
  2. echo -e "#!/usr/bin/env bash\ncat /etc/bandit_pass/bandit24 > /tmp/\"$(echo Hello my fellow mates | md5sum | cut -d ' ' -f 1)\" > script" to create the script that we'll use to print the password in our dedicated file.
  3. chmod o+x script to give bandit24 permission to execute the script
  4. cat /tmp/"$(echo Hello my fellow mates | md5sum | cut -d ' ' -f 1)" to retrieve the password

Bonus : Creating a file inside the directory

We learned how to create a file to store the password in, let's now go one step further and see if we can figure out how to create a script that creates a file containing the password within our temporary directory.

Hint

Using what we did before and the Helpful Reading Material, can you figure out a way to write a script that will be able to create a file in our temporary directory? You will have to make another call to chmod to get all the file permissions right.

Solution

Here is our script :

#!/usr/bin/env bash

cat /etc/bandit_pass/bandit24 > /tmp/hello_fellow_mates.1JNCwI8su9/bandit24_pass

Let's run a quick stat, but this time on our directory :

bandit23@bandit:/tmp/hello_fellow_mates.1JNCwI8su9$ stat -c '%A Uid: (%u/%U)  Gid: (%g/%G)' .
drwx------ Uid: (11023/bandit23)  Gid: (11023/bandit23)
bandit23@bandit:/tmp/hello_fellow_mates.1JNCwI8su9$

Here we can see that others don't have any right to write to the directory nor to access its files. Let's call chmod on our directory to set the right permissions :

chmod o+wx .

Then, by putting our script back into the /var/spool/bandit24/foo directory, we will see the file bandit24_pass created after the cronjob execution.

You can now jump to the next level

Bandit24->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

Commands useful to solve the level

Helpful Reading Material

Where to start?

We already know that there is a daemon listening on port 30002 which listens for our password. The goal here is to find an efficient way to brute-force the password for the next level.

Part 1 : Getting to know the daemon

This part will be pretty short as we already have experience with daemons (see bandit14 for more explanations). We will simply connect and try to communicate with the daemon to see how we should speak with it.

Hint

By using the nc utility, can you figure out the format of the string you should send the daemon in order to craft your brute-force attack?

Solution

Using nc, we can speak with the daemon and run the following tests :

bandit24@bandit:/tmp/abcdef.PtK5$ 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.
bandit24_password 0000
Wrong! Please enter the correct pincode. Try again.
bandit24_pasword 0001
Wrong! Please enter the correct pincode. Try again.
^C
bandit24@bandit:/tmp/abcdef.PtK5$ 

Using that information, we know the format of the string we're supposed to send to the daemon to try to bruteforce the password. Let's know try and use this knowledge to craft our brute-force attack.

Part 2 : Crafting the attack

Now that we know how to communicate with the server and that we noticed that indeed, we don't have to open a new connection for each message, let's try to generate all the password/pincode combinations for our brute-force attack.

Hint

Using the Brace Expansion, the Looping Constructs and the printf sections of the gnu bash manual, can you figure out a way to generate all the combinations for our attack?

Solution

You'll be able to find a lot of solutions following the same pattern all over the internet. Let's try to do something a bit different.

We are going to use a for loop, but not the one that depends on a pattern, the one that depends on an arithmetic expression.

Here is what our loop is going to be :

for (( i=0 ; i < 10000 ; ++i )) ; do printf "%s %04d\n" "bandit24_pass" "$i" ; done

Here is a detail of what our loop does :

  1. For all the integers between 0 and 9999 it does the following :
  2. It prints the string bandit24_pass alongside the value of the integer (padded with zeros to fit a field width of 4 characters)

Part 3 : Launching the attack and retrieving the password

Now that we know what our for loop looks like, you might want to know why we used this construct instead of the first form. Let's not wonder about that for now and instead launch the attack.

Hint

Using our newly constructed for loop, can you figure out a way to use nc to retrieve the password?

Solution

Here is how we are going to use our for loop to retrieve the password.

for (( i=0 ; i < 10000 ; ++i )) ; do printf "%s %04d\n" "bandit24_pass" "$i" ; done | nc -w 10 localhost 30002

This loop will test all the 10000 strings against the server pin and will be enough to retrieve the password.

The -w option of nc allows to specify a timeout in case the connection becomes idle. If the timeout is reached, the connection will be closed.

Part 4 : Let me think please

If this hasn't been patched yet, you might notice that the server blocks indefinitely after a given number of attempts. The goal of this last part is to ensure that the server won't block and that we'll be able to test all the connections.

Hint

Using our command from the last part, would you be able to add a simple check to ensure that the server doesn't test all the attempts at the same time but waits a bit before sending each chunk of tests.

Solution

Here is the updated command :

for (( i=0 ; i < 10000 ; ++i )) ; do if (( $i%500 == 0 )) ; then sleep 1 ; fi ; printf "%s %04d\n" "bandit24_pass" "$i" ; done | nc -w 10 localhost 30002

The if check ensure that the server gets time to process the input, ensuring that it won't block after a given amount of requests.

Full Solution

  1. for (( i=0 ; i < 10000 ; ++i )) ; do if (( $i%500 == 0 )) ; then sleep 1 ; fi ; printf "%s %04d\n" "bandit24_pass" "$i" ; done | nc -w 10 localhost 30002 this command will test the password and all of the 10000 pin combinations agains the server pin and prints the password for the next level once the right pin has been entered.

Don't forget to replace bandit24_pass with the actual password for the bandit24 user.

You can now jump to the next level

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

Commands useful to solve the level

Helpful Reading Material

Where to start?

By listing the contents of the directory, we can see that there is a private key that we should use to log in as the user bandit26, this means that this isn't where the true challenge of this level is. Let's try and analyse the level following the guidelines at the beginning.

Part 1 : Retrieving bandit26 shell

The first thing we need to do is to retrieve bandit26 shell, as we know that this shell is not /bin/bash.

Hint

Using the passwd(5) man page, can you figure out a way to retrieve the shell that bandit26 gets when it logs in and to view its contents?

Solution

Using the passwd(5) man page, we know that the informations for the bandit 26 user are stored in the /etc/passwd file. This file is readable by everyone so we can print its content and grep only the lines containing bandit26. Here is the command we'll run, alongside its output :

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

We know from the passwd(5) man page that the last field is the bandit26 user's shell : /usr/bin/showtext.

Let's print the contents of this file :

#!/bin/sh

export TERM=linux

exec more ~/text.txt
exit 0

We see here that the script sets one variable TERM and then runs the more utility.

One thing we can already notice is that the showtext executable doesn't take any argument, so we won't be able to ssh our way into bandit26 account running a command like we did in level18 (see how ssh commands are run for more explanations). We'll have to find another way to get in.

Part 2 : Let's scroll that thing

When we first ssh our way into bandit26, we see that the showtext executable is ran. It prints the text bandit26 in ASCII art and then exits.

We already know that the showtext executable uses more, the real challenge here is to take advantage of the more capabilities to run commands. To do so, we need to make it scrollable so that it shows its command prompt.

Hint

By doing some tests with files on your own computer, can you figure out when more is scrollable and where it isn't? Doing so, could you make it scrollable when logging in into bandit26 and find which command to run to get a text editor?

Solution

Although it is not very intuitive, you might have noticed that when the window size is smaller than the number of text lines, more becomes scrollable. We're going to use this capability of the more utility to break out of it. Let's minimize our window to less than 6 lines and then ssh into bandit26.

There might be a more elegant solution through the use of a terminal multiplexer like tmux but the idea will basically be the same. I'll provide a solution using tmux(1) once I learn to use it.

We can now enter commands (see more man page for the full list). We are going to use the v command in order to open the vim editor.

We can now bring our window size back to normal.

Part 3 : Byebye showtext

Now that we got inside vim, we have to get rid of this nice yet useless shell and get a real one.

Hint

Using the various remaining commands file of the vim help manual, can you figure out a way to get a shell while in the vim editor?

Solution

The command :shell is the one we need. Remember that you need to press <ESC> first to get into normal mode. However, when running it for the first time we can see that nothing seems to happen. The truth is that something really happened in front of our eyes. The shell of the user bandit26 was launched and then exited as it is the showtext executable.

To convince yourself that it really happened, you can minimize the window to less than 6 lines before running the :shell command

We now need to change the default shell for user bandit26 in order to finally get out of that showtext hell.

Part 4 : Getting bash back

We now know that we can run a shell, let's try to change the default shell to /usr/bin/bash in order to actually run a shell.

Hint

Using the Vim Documentation Options, can you figure out how to view, then change, the shell we're using when running the :shell command?

Solution

The command we're looking for is :set which allows us to view/change settings for the options we specify.

By running :set shell we can view the shell we're using (which in our case outputs /usr/bin/showtext).

To change the shell, we just have to run :set shell=/usr/bin/bash, we can then run :shell and get a shell for the user bandit26.

Full Solution

  1. Minimize the window and ssh into bandit26 account to make the more utility scrollable.
  2. Enter the v command to open the ViM text editor
  3. :set shell=/usr/bin/bash in order to set the default shell to a real shell
  4. :shell to open the bash shell for the user bandit26

You can now jump to the next level

Bandit26->27

Level Goal

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

Commands useful to solve the level

Helpful Reading Material

Where to start?

This level is the exact same as the level 19. If you need any information, go check this level.

Full Solution

  1. ./bandit27-do cat /etc/bandit_pass/bandit27 to print the bandit27 password to standard output.

You can now jump to the next level

Bandit27->28

Level Goal

There is a git repository at ssh://bandit27-git@localhost/home/bandit27-git/repo via the port 2220. 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.

Commands useful to solve the level

Helpful Reading Material

Where to start?

Here we enter a series of levels that are aimed to teach us the git basics. Let's dive right in and solve the first level

Part 1 : Getting the repository

Our first challenge in our quest to master Git is to unterstand how to get a copy of a guide repository (or clone).

Hint

By doing the first part of the gittutorial and reading the git-clone man page, can you figure out a way to retrieve the repository located at ssh://bandit27-git@localhost/home/bandit27-git/repo ?

Note : You might need to create a temporary directory

Solution

Lets first change directory to a temporary directory with cd "$(mktemp -d /tmp/bandit27git.XXXXX)", then we can run the following command :

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

Remember that we have to specify the port as we're not connecting using the port 22 (ssh default's port) but the port 2220.

We can then enter the password for bandit27-git (which is the password for bandit27) and we see the repository repo appear in our temporary directory.

We can now try to retrieve the password.

Part 2 : Retrieving the password

For this level, the password retrieval is pretty straightforward.

Hint

Try listing the contents of the directory.

Solution

By running cat README.md we can retrieve the password for the next level.

Full Solution

  1. cd "$(mktemp -d /tmp/bandit27git.XXXXXX)" to change to a temporary directory
  2. git clone ssh://bandit27-git@localhost:2220/home/bandit27-git/repo to clone the repository into our directory
  3. cat README to retrieve the password

You can now jump to the next level

Bandit28->29

Level Goal

There is a git repository at ssh://bandit28-git@localhost/home/bandit28-git/repo via the port 2220. 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.

Commands useful to solve the level

Helpful Reading Material

  • giteveryday A useful minimum set of commands for Everyday Git

Where to start?

For more informations about how to clone the repository, see the previous level.

From now on, I'll assume that you already retrieved the git repository in your temporary directory.

Part 1 : Viewing the history

In this level, when we cat the README.md file in the directory, we have a series of x's instead of the password like in the previous level. Of course, this series of x's isn't the password so we'll need to find a way to retrieve it.

Hint

As git stores the whole history of the file modifications, looking at the git-log man page, can you figure out a way to view the history of the git repository?

Solution

By running the git-log command, we can see that the commit history talks about missing data that has been added and the commit we're on talks about a memory leak. Our next goal will be to check for differences between the HEAD which is the point we're on in the history (usually after the last commit) and the commit that talks about missing data.

Part 2 : Retrieving the password

Now that we know where the information we'd like to retrieve might be, we need for a way to check if this information is actually there.

Hint

Looking at the git-show man page, can you figure out a way to view the differences between the README at the current commit and the README at the previous commit?

Solution

Using the git-show command, we can provide the hash of the commit we want to view

Note : We don't need to provide the full hash and the 5 first characters are usually enough

Let's run the following command in our terminal :

git show f08b9

This will print the last change in the README.md file, thus printing the password string.

Full Solution

  1. git log to view all the commit history.
  2. git show f08b9 to view the difference with the previous commit.

You can now jump to the next level

Bandit29->30

Level Goal

There is a git repository at ssh://bandit29-git@localhost/home/bandit29-git/repo via the port 2220. 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.

Commands useful to solve the level

Helpful Reading Material

Where to start?

Once again, I'll assume that you already cloned the repository (see bandit27 for more informations).

Part 1 : Viewing all the branches

When we try to view the log and to show the differences in the repository, we don't get any relevant information.

Another great capability of git is the ability to branch. A branch is a line of development which is totally independent from all the others from the point when it has been created.

Hint

By looking at the git-branch man page, can you figure out a way to list all the branches in the repository?

Solution

We want to list all the branches of the repository. Let's run the following command :

git branch -a

This will list all the local branches (which is only master at the moment) and all the remote tracking branches. The following command outputs 3 branches : dev, master and sploits-dev. Let's now see if we can retrieve the password in one of these two other branches.

Part 2 : Viewing the differences between the branches

Now that we know that there are multiple branches, we'll try to view the differences between the README.md file and the files on the other branches

Hint

Looking at the git-diff man page, can you figure out a way to view the differences between the master branch and the other branches?

Solution

Let's try and run the following command :

git diff remotes/origin/dev

We need to use remotes/origin/dev because the dev branch is not tracked locally. To track the remotes/origin/dev locally you'd have to run git checkout dev first.

When running the following command, we can see that the password is on the dev branch and use it to connect to the next level.

Full Solution

  1. git branch -a to view all the branches in the repository
  2. git diff remotes/origin/dev to view the changes between the dev branch and the master branch

One key takeaway of this level may be the Git mantra : branch early and branch often

You can now jump to the next level

Bandit30->31

Level Goal

There is a git repository at ssh://bandit30-git@localhost/home/bandit30-git/repo via the port 2220. The password for the user bandit30-git is the same as for the user bandit30.

Clone the repository and find the password for the next level.

Commands useful to solve the level

Helpful Reading Material

Where to start?

Once again, if you need information about retrieving the repository, go check bandit27 level.

Here, we can notice that nothing we did precedently works. As we had to do with the other levels, we have to learn another git capability and use it to retrieve the password.

Part 1 : Viewing the points of interest in the history

One capability of git is to tag some commits as important, let's see if we can use it to our advantage and retrieve the password.

Hint

Using the git-tag command, can you figure out how to view all the tags of the git repository?

Solution

Let's run the following command :

git tag

This allows us to list all the tags within the git repository. This command outputs a secret tag which seems to be the door that separates us from our password.

Part 2 : Retrieving the information

Now that we got our tag, we have to retrieve the information that's hidden within it.

Hint

Using again the git-show man page, can you find a way to retrieve the informations within the secret tag?

Solution

Let's run the following command :

git show secret

This command let's us view the informations that were added during the tag creation. Of course they include this level password, thus allowing us to jump to the next level.

Full Solution

  1. git tag to list all the tags in the repository.
  2. git show secret to view the informations within the secret tag and retrieve our password.

You can now jump to the next level

Bandit31->32

Level Goal

There is a git repository at ssh://bandit31-git@localhost/home/bandit31-git/repo via the port 2220. The password for the user bandit31-git is the same as for the user bandit31.

Clone the repository and find the password for the next level.

Commands useful to solve the level

Helpful Reading Material

Where to start?

When we cat the README.md file, we see that it gives us a set of instructions. We have to create a key.txt file and "push" it to our remote repository.

Part 1 : Stagging the key.txt file

We need to create a file called key.txt with the contents 'May I come in?'. The real challenge here will be to stage the key.txt file for commit.

Hint

Using the git-add man page and the Gitignore Documentation, can you figure out a way to stage the key.txt file?

Solution

When we run git add key.txt, we get the information that the key.txt file has been marked as ignored by git (you can cat the .gitignore file in order to understand why. Thus running the following command :

git add -f key.txt

This allows us to tell git to add the key.txt file even if git has been told to ignore it.

Part 2 : Pushing the key.txt file to the repository

Now that we've added the key.txt file to the stagging area, we need to push the file to the repository to validate the challenge.

Hint

Using the git-commit and the git-push man pages, can you figure out how to push the file to the repository?

Solution

Each commit must have a, preferably explicit, commit message. Then after taking our snapshot of the state of the repository, we can then push our set of snapshots to the remote repository. We can use the -m option of git commit to specify the message on the command line.

Here are our two commands :

git commit -m "explicit commit message"
git push

As we followed all the instructions, we get the password for the next level in the response.

Full Solution

  1. echo "May I come in?" > key.txt to create the key.txt file.
  2. git add -f key.txt to forcefully add the key.txt file to the stagging area.
  3. git commit -m "explicit message" to commit the changes
  4. git push to push our changes to the remote repository

You can now jump to the next level

Bandit32->33

Level Goal

After all this git stuff its time for another escape. Good luck!

Commands useful to solve the level

Helpful Reading Material

Where to start?

To be honest I don't have the same experience with the uppercase shell than the one I had with the showtext executable. I hated showtext because it litteraly did nothing and wasn't acknowledging my inputs when the uppercase shell seems to be yelling and trolling me and I find that kinda funny. Let's now dive into the challenge.

The problem seems pretty obvious to spot, everything we type is converted to uppercase, so our first goal is to understand the shell we've been given.

Part 1 : Getting to know the uppercase shell

We'll start by trying to input some commands and get to know what is the uppercase shell really doing. Then, using that insight, we will try to figure out what we can actually do with that shell.

Hint

By testing and trying to run some commands, can you describe the effects of the uppercase shell and can you see what type of input will be unaffected by the effects of that shell?

Solution

Now that we made some tests with the uppercase shell, we can notice that it converts all of our input to uppercase before feeding it to an actual shell. From that, we can deduce that the only input that will stay unaffected by the effect of this shell will be shell variables (as they are usually already uppercase).

Part 2 : Getting an actual shell

Knowing that shell variables are unaffected by the uppercase shell, we'll need to use that feature to get an actual shell.

Hint

Using the special parameters section of the gnu bash manual, can you figure out which variable to use to get a shell?

Solution

As the uppercase shell actually converts our input to uppercase before feeding it to an actual shell, the $0 variable contains the name of the shell it invokes. Thus, by running the following command :

$0

We can then get an actual shell and use it to cat the bandit33 password.

Full Solution

  1. $0 to get an actual shell (which will in fact be sh)
  2. cat /etc/bandit_pass/bandit33 to get the password for the next level.

You can now jump to the next level

Thank you Overthewire

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!