After "Ellingson" made me realize that I am still a bit lacking in the Binary Exploitation department, I decided to put some focus on improving my skills in that area. I was told that "OverTheWire" is the place to go for that, so I went there.
The first set of challenges is called "Bandit", and it's more of an introduction to the Linux CLI than anything else. BUT, it's the first wargame there, so we might as well start with that.
The 0th level requires us to connect to the wargame via SSH. A rudimentary SSH call would be
The side tells us the username we need to use, "bandit0", and the port, 2222. So the call would be like this:
We will be ask for a password, which is also provided by the site - "bandit0", the same as the username. If we enter that, we will be greeted with a new prompt:
bandit0@bandit:~$
This tells us that we are logged in as "bandit0" on a machine with the hostname "bandit".
Now that we have a foothold on the machine, we should get a feeling for our "surroundings", so to speak, and what we can access. For that, two commands are essential: pwd and ls.
The output of pwd tells us the absolute path of the directory we're currently in:
The output of ls tells us which files and directories reside in the current directory. I personally always use ls -l -a - or rather the abbreviated version ls -la - for the following reasons:
Nothing out of the ordinary here, apart from the readme file. But first, let's take a closer look at the output and what it means, taking the last line as an example.
The first column of the output, -rw-r----- tells us about the permissions on the file. The first character is for special bits (e.g. SUID, or "d" indicating that we're looking at a directory rather than a file). The other 9 characters are pairs of 3 referring to the permissions of the owning user, the owning group and everyone else respectively. For the readme, we can see that the owning user has read (r) and write (w) permissions, the group has only read (r) permissions and everyone else has no permissions at all on the file. There is a third kind of permission, execute (x), but no such permissions is set for the readme file.
The third and fourth column of the output, bandit1 and bandit0 tell us about the owning user and the owing group respectively. As a takeaway, we can see that the owning group is "bandit0", the group we belong to (more on that further down), which, in combination with the information about permissions, means that we are able to read the contents of the file.
The fifth column, 33 is the size of the file in bytes.
The sixth and seventh column, Oct 16 and 2018 together form the date of the last modification.
And finally, the eighth column, readme, represents the filename.
Side note: In the above ls output, there are two directories: "." and ".." - these are special directories in Linux referring to the current directory(".") and the directory one level above("..") respectively. As the pwd command showed us that we are in "/home/bandit0", ".." is essentially the same as "/home".
Earlier I said that we are part of the group "bandit0" - how do we know that? Well, the command id tells us:
uid tells us our user id, followed by the username in brackets.
gid tells us the id of our "main" group, and groups gives us a list of all the groups we belong to, which is currently only bandit0.
As that one file tells us to read it, we shall do so. There are a lot of command-line tools that can be used for that, like vim, nano, more or less, but for just reading a short file like that i prefer to use cat:
A password! The wargame page for Level 0 -> Level 1 tells us we need to get the password from the readme file to get to Level 1. As we now have that, we have officially completed Level 0!
With the password from Level 0, we can now get to Level 1 using SSH:
Just in case you're wondering why, when you type the password, it doesn't show up on screen: That's a built-in security feature by Linux, that prevents any shoulder-surfers from getting your password.
Anyway, now that we are in Level 1, we start with the same procedure as in Level 0 - figure out where we are and what we can see:
Interesting - a file called "-". Let's use cat on it and grep the password:
Hmm, nothing. Though we can see that the file has a size of 33 bytes, so there should be something in there. The problem here is the following:
"-" is a legitimate filename in Linux. But it also refers to the stdin, the standard input. And this is what our cat command thinks we are referring to. So it will just reflect whatever we write back to us. That isn't what we want. So, to fix this, we execute the following:
It works! But why? Well, as explained in Level 0, "." refers to the current directory, so "./-" just refers to the file "-" located in the current directory. But when writing it like this, we avoid any ambiguity with the placeholder for stdin "-", and cat knows what we are actually referring to!
Another level done!
The SSH command to get to a level always follows the same pattern, so this will be the last time I write it out here. In the next levels, I am just gonna start after I logged in via SSH.
As always, let's have a look around:
There's the password file, let's just read it and move on:
Would have been too easy, wouldn't it? Well, in the command line, spaces separate parameters. So if we enter the command like above, cat thinks we want to read four files: "spaces", "in", "this", and "filename". To fix this, there's a simple trick: Quotes. Quotes tell cat that everything between them is one single string. So in order to read the file, we would have to write something like this:
Level 3 password: Acquired!
Note that it doesn't matter whether we use double quotes or single quotes, we just have to use them pairwise. That means this will work:
bandit2@bandit:~$ cat 'spaces in this filename' UmHadQclWmgdLOKQ3YNgjWxGoRMb5luKBut this:
bandit2@bandit:~$ cat "spaces in this filename'or this:
bandit2@bandit:~$ cat 'spaces in this filename"won't.
Let's see what we have:
Here we can see that there is a directory called "inhere", which probably contains the password file for Level 4. In order to grab it, we need to change directory, or cd, for short:
Ha! Our insistence on using the -a parameter for -ls is already paying off :D Let's grab the password and move on:
Some notes on directories in the command line interface: The prompt always shows us the directory we're currently in. "~" represents the home directory of the current user, in our case "/home/bandit3". If we change the directory, the prompt will reflect that. After we execute cd inhere we can see that the directory in the prompt also changes to "~/inhere". This is very useful for orientation in the directory structure.
The pwd command is still a useful tool and should always be on the back of our minds, for three reasons:
- On foreign or new systems, we might not always now that the home directory of a user is, so we wouldn't know what "~" stands for.
- Command prompts can be configured to not show the directory you're currently in.
- If you're reading this block, there's a good chance you're interested in hacking. When you "get a shell", you usually don't get a prompt at all. So pwd is a must if you want to know where you are.
Same old, same old:
From Level 1, we already know that dashes at the beginning of filenames are tricky. But we also know how to get around that.
But, as the "-" is not the whole filename this time, the behavior is a little bit different this time, so I want to elaborate on that a bit:
bandit4@bandit:~/inhere$ cat -file00 cat: invalid option -- 'f'This time, cat doesn't wait for any input from us, it just gives an error and exits. This is because in Linux, it is convention to pass multiple parameters after the following scheme:
[command] -[parameter_name] [parameter_value]Not all parameters need a value, so the [parameter_value] part is optional. But looking at that scheme, we can deduct that if we execute cat -file00, "file00" is treated as the name of a parameter, rather than a file.
One of those files will contain the password. But it would be terribly tedious to read every file one at a time. Thank Goddess there's a way around that - "*". "*" is a placeholder character, that allows us to output all the files in the directory at once. Combined with our "./"-trick, we get the following:
We will get a lot of garbled output, because most files contain bytes that don't translate well into UTF-8-characters. But we can see one string in there: "koReBOKuIDDepwhWk7jZC0RTdopnAYKh". Most likely, this will be our Level 5 password (Spoiler: it is).
So with that, Level 4 is officially done!
Okay, this is gonna be a bit more complicated. We get some information from the Level 5 -> Level 6 page, though. Our file is:
With this information, we can use find to - you guessed it - find the file we're looking for. We just need to pass the given information as parameters. The final command call then will look like this:
Yes, yes, I know, we didn't use the information "not executable". But we didn't need it. There's only one file matching the other criteria. Slight caveat: -readable does not mean "human-readable", it just means we can view the contents of the file. It could just as well be a garbled something, like in Level 4. But it's another. Another note: We need to specify -size 1033c, rather than -size 1033, as the latter would look for a file with a size of 1033 512-byte blocks. The "." just after the find means we only look in the current directory and its sub-directories.
Let's get the password:
Onto Level 6!
Nothing out of the ordinary, so let's take a look at the Level 6 -> Level 7 page. Seems like it's similar to Level 5, just that we have to look on the entire filesystem, rather than just the current directory and its subdirectories.
Now, we COULD just call something like this:
But that would just result in a mess of error messages (along with the correct file). That's not really nice to look at. So, instead, we're gonna add a little something at the end of our command, 2>/dev/null. Now, what does this do? In this context, "2" refers to stderr, or "Standard Error". This is the file descriptor where error messages are normally directed towards. The ">" then redirects everything that would normally land in stderr. And where do we redirect it to? /dev/nuull, which is basically a "void" of some sorts, getting rid of everything that goes in there.
Adding that, we get rid of all the error messages and leave only the desired output:
Level 6? Check!
The interesting file here is "data.txt", but it's gigantic! It would take days to read through all of it and find the password. Thankfully, there's grep, which allows us to search for a specific word or text in a giant amount of data. And the Level 7 -> Level 8 page tells us which word we need to look for: "millionth".
There we have our password! But, before we move on, some words on what happens in that command there:
cat data.txt just outputs the contents of "data.txt", as we know. The | operator allows us to perform the command to the right side of it on the output of the command to the left. grep millionth then looks for the word "millionth" in a given text and then outputs the entire line the word occurs on. If the word would occur multiple times, we would get multiple lines of output.
According to the Level 8 -> Level 9 page, this is gonna be a little bit more complex than level 7.
I think it is easier to start with the solution and then explain why it works.
So, what happens?
Easy as Raspberry Pi - let's move on.
Reading the Level 9 -> Level 10, we know that we must search for human-readable strings that start with several "=" characters. With strings, we can get a list of all the human-readable strings in a file. With grep === we should then get what we want, as "===" can be classified as 'several "=" characters' :D
DONE!
The Level 10 -> Level 11 page tells us that we have base64 encoded data, so we need to decode it. Luckily, there's a tool for that: base64.
Here, we need to decipher text that is rotated by 13 positions (ROT13), as the page tells us.
For that, we use tr, a tools that helps to modify and/or "translate" text.
The tr call replaces the output of "data.txt" according to the to character sets given to it. Every "A" is replaced by an "N", every "B" by an "O", and so forth. This is exactly what the ROT13 cipher does.
Let's start with what the Level 12 -> Level 13 tells us to do.
Create our own directory under /tmp using mkdir, copy the "data.txt" file there using cp and then rename it using mv. But, as cp has renaming capabilities of its own, we'll just skip the mv step.
We then turn the hexdump back into an actual file using xxd:
Reminder: > is the redirect operator. If we don't specify a file descriptor (like "2" in Level 6), it instead redirects the stdout - or Standard Output. If we specify a filename on the right side of that operator, it gets redirected to that file.
Some notes: If you use >, a new file is created. So if you specify the name of an already existing file, it gets overwritten. If you want to append instead, use >, so for example
cat data.txt.dump | xxd -r >> data.txt
The description tells us something about "compression", though, so let's find out how the file has been compressed using file:
file gives us some information about a file, most importantly the filetype. We can see that "data.txt" is the result of running a gzip-compression against a file called "data2.bin". Let's retrieve that file using gunzip:
Oh, yeah, right, gunzip requires the filename to end with ".gz" in order to work:
If we call gunzip without any other parameters, it deletes the original file and the newly created file is named similar to the input file, just without the ".gz" - meaning the result of our decompression is "data.txt".
We still don't have a text file though, just a bzip2-compressed file. So let's decompress that as well:
Still not a text file, so let's keep decompressing:
A new kind of compression, tar. Let's unpack that as well:
After some more rounds of compression, we finally have our password!
We have an interesting file here: "sshkey.private". Most likely, it is the private key of a Public-Private Key-Pair. You see, instead of a password, the (usually) safer option is to use a private key for authentication. The private key is only known by you, with a size of several thousand bytes, and the public key can be made known to several SSH servers, that you then can authenticate against using your private key. For SSH, the parameter -i allows us to pass a private key. Understanding all that, we can proceed to Level 14:
Network time! According to the Level 14 -> Level 15 page, we have to submit the current password (which can be read from /etc/bandit_pass/bandit14 according to the Level 13 -> Level 14 page) to port 30000, and get the next password as a response. For this, we can use netcat:
There we have our Level 15 password!
According to the Level 15 -> Level 16 page, we now need to do something similar to what we just did, but this time we need to pass the password using SSL encryption. For this, we can use s_client:
Password acquired, let's continue!
Level 16 introduces us to one of the most important parts of every hacker toolkit: nmap. According to the Level 16 -> Level 17 page, there is a port somewhere between 31000 and 32000 that speaks SSL and to which we need to talk just like we did back in Level 15. So, let's find that port:
The parameter -p31000-32000 tells nmap that we're only interested in ports in that range, saving us some time that would otherwise be wasted on scanning all the other ports. Two ports are open, but only one of it gives us the answer we want, so, let's find out:
So this first one just echoes back what we're sending it, so not what we're looking for.
This is the juicy stuff. Just like in Level 13, we can use it to advance to the next Level. First, however, we need to write it to a file. Let's do it on our local machine so we can get back to this level later, if we ever feel like it. First, we highlight the key, right-click it and copy it to our clipboard. Then, we add it to a file:
Then, we can use the newly created file, "bandit17.rsa", to advance to the next level:
Or can we? The private key needs to have secure permissions in order for the ssh command to accept it. "Secure" here means that it is only readable, and only by the owning user. We can do this using chmod.
chmod can change the permissions of a file or a directory. It represents those permissions as a set of 3 numbers. The first number represents the permissions of the owning user, the second that of the owning group and the third that of everyone else. "1" means execute rights, "2" means write rights and "4" means read rights. The sum of the numbers than sets the overall permissions. For example, "560" would mean: The user has execute and read rights (4+1=2), the group has write and read rights(4+2=6) and everyone else has no rights regarding the file or directory(0).
Et voila, Level 17 achieved!
The Level 17 -> Level 18 page tells us that between "passwords.new" and passwords.old" there is only one line that's different, and that is the password for the next level. With the diff tool, we can easily compare files and find their differences:
This tells us that the line "hlbSBPAWJmL6WFDb06gpTx1pPButblOA" in "passwords.old" has been changed to "kfBf3eYk5BPBRzwjqutbbfE887SVc5Yd" in "passwords.new", so the latter must be what we're looking for.
Well, that's sad. According to the Level 18 -> Level 19 page, we get immediately logged out if try to connect via SSH. And, as we can see, that is exactly what happens. Luckily, the page also tells us which file contains the password AND ssh gives us the opportunity to execute commands without having a shell. Using that, we can achieve what we're trying to achieve:
If we just add a command at the end of our ssh call, the command is executed and the sessions ends, without the need of actually opening a shell.
This level introduces us to a vector for Privilege Escalation which is relatively common when it comes to hacking Linux systems: SUID.
The Level 19 -> Level 20 page tells us that we have to use the "bandit20-do" binary to read the password.
Side note: If we look at the permissions of that file, "-rwsr-x---", we can see that there is an "s" where the "x" is supposed to be. This just tells us that the SUID bit is set. If this bit is set, the program is always executed with the permissions of the owning user (here: "bandit20"), no matter who starts the program.
If the permissions looked like this: "-rwxr-s---", the program would always be run in the context of the owning group (here: "bandit19") instead.
Let's take a look at what the executable actually does:
We can run EVERY command as bandit20, so we can just output the content of the password file:
More network fun. The Level 20 -> Level 21 page tells us that we need to execute "suconnect", connect to a port on the local machine that returns the password for Level 20. For this, we need two terminals open who both have a SSH connection to the machine as bandit20. On the first, we will return the password, on the second, we will run "suconnect".
First, let's use netcat on the first terminal to listen for incoming connections:
The -l tells nc to listen for any incoming connections, and -p 55123 specifies the port to listen on. -v means verbose output, to get some feedback on what's happening. Note that the above is just an abbreviated form of:
I chose the port at random, any port that is not in use is good here. Be ware that we would need root permissions (which we don't have) for any port below 1024.
On the second terminal, we now start the "suconnect" program:
We can then see that netcat received a connection:
If we then send the Level 20 password, we get the Level 21 password in return. The whole output in the first terminal from starting netcat to getting the password then looks like this:
Here we get to see another vector that is open to attack in PenTesting/Hacking Scenarios: According to the Level 21 -> Level 22 page, a cronjob is regularly running an executing a command. Let's take a look at that, shall we?
Let's step by step examine what's happening here:
First, we look at the "/etc/cron.d" directory, where we can see several cronjobs. The interesting one here would be "cronjob_bandit22", as Level 22 is the one we are currently aiming to get to.
The config tells us that there is a job running on the regular, which executes the file "/usr/bin/cronjob_bandit22.h". Looking at that file using ls -la we can see that we have read permissions, and use that to examine the content. This bash script sets a the permissions of a file in the "tmp" directory to be readable by everyone and then outputs the contents of the file "/etc/bandit_pass/bandit22" to "/tmp/t7O6lds9S0RqQh9aMcz6ShpAoZKF7fgv". If we read that file's contents, we get the password for Level 22!
According to the Level 22 -> Level 23 page, we basically need to do the same as in Level 21:
As this is basically the same as the first half of Level 21, I won't go into detail here. What is different though is the bash file that's executed. When the cronjob runs, whoami will return "bandit23". With that, we can reconstruct the code for "mytarget" and get the filename of the password.
Those wondering what md5sum does: MD5 is a hashing algorithm, a one-way algorithm that returns a value of fixed size.
Reading the Level 23 -> Level 24 page, we can see that we - again - need to start by examining the correct cronjob.
The interesting part is - yet again - the bash script.
Being the friendly script that it is, it actually tells us what it does: It executes every script in "/var/spool/bandit24" (and then deletes them). So we can just deposit a script there that copies the bandit24-password to a file in "/tmp" that we can read:
This short little script copies the contents of the bandit24 password file to "/tmp/ti-kallisti.txt" and makes it readable for everyone.
The first line of the script is called a shebang. It tells the linux command line which interpreter to use when executing the file.
We still need to make it executable before it can run:
Once the cronjob has run, we'll get our password:
According to the Level 24 -> Level 25 page, this Level introduces us to the concept of bruteforcing. Bruteforcing means that we try every single possibility until we get the right one. To do this manually would be tedious and incredibly time-consuming, so of course we're gonna write a script for that:
This will go through all values between "0000" and "9999", print them next to the Level 24 password as demanded and then send them to the port 30002.
The seq command returns a list of numbers between 0 and 9999, while the -f $04g parameter tells it to prepend leading zeroes to make it always 4 characters long. The "`" around it makes it so that the output of the command is used, rather than the command string itself.
We first need to make the script executable:
The +x parameter just means that we give ourselves executable rights on that script.
Then we run it, but with a catch:
We already know the grep command from previous levels. But with the -v parameter it does the opposite of what it usually does: Instead of only printing the lines containing a specific string, it prints only the lines NOT containing that string. It helps filter the output for wrong passcode attempts.
This is a bit different. We immediately get the keys to Level 26, but the Level 25 -> Level 26 page tells us that we don't get a normal "/bin/bash" shell. The "/etc/passwd" file tells us which user uses which shell:
Apparently, a program called "/usr/bin/showtext" is used as the default shell for bandit26. So that's what's gonna be executed when we try to connect using SSH. Let's take a look at that:
The exit 0 part is a bit concerning. It will kill our shell the moment we try to connect. But, there's a neat little trick to get around that: If you reduce your terminal window to minimal size, more will not exit before it has shown you all of the text. And that's when we strike:
According to the more man page, if we press v while in the more window, we will drop into an editor. Usually, this is either vim or nano. Both will allow us to get an actual shell. A good source on how to get out of an editor (or in other cases, jailed shells), is GTFOBins.
So, if we do that trick, and press v we can see that we drop into vi. From here, we can use the GTFOBins knowledge, enter
And have a shell. whoami shows us that we are bandit26, and pwd shows us that we are in "/home/bandit26". (This is one of those examples I mentioned in Level 3 where a prompt doesn't give us information about who or where we are).
This Level is just there to get us out of this horrible shell:
The solution is the exact same as for Level 19:
This Level introduces us to git, as the Level 27 -> Level 28 page tells us. We just need to clone a repository and grab the password:
The password for cloning the git repository is the same as the password for Level 27 itself.
Curiously, according to the Level 28 -> Level 29 page, this is nearly the same as Level 27.
Well, not exactly the same. git is a version control tool, so the obvious idea here is that sometime in the past the password stood there in cleartext, but was then removed.
A look at the logs confirms our assumption:
So we need to recover an older version of the file. Or, we just look at the changes made in that specific commit:
There we have the password before it was "obfuscated".
Another git level. I won't paste the commands for checking out the repository here, as this is the same as before.
If we check the logs, we can see that this won't be just as easy as Level 28.
The "README.md" is giving us a hint, though: "no passwords in production!". So maybe there's a development branch?. Let's check it out:
We have several branches that are not production branches. Let's check out the "dev" branch and see if that contains a password:
There we have it!
Another git level, so let's skip ahead, like before.
No password, no past changes. This is gonna be a bit trickier.
Not even a different branch that could contain our password.
Taking a look around shows us that there is a tag called "secret".
We get confirmation when we execute the following:
Now, how to get the information we need? To see the content of a tag, we can use git's show command:
Believe it or not, this is the password for the next level. We did it!
[Insert git clone procedure]
This level introduces us to actually putting files to the remote git repository, using push.
The "README.md" is so friendly that it tells us exactly what to do.
First we create the file:
Then we need to add it:
I mean we REALLY need to add it:
Then we commit our changes with a nice little message:
And then we push:
Well, it doesn't actually work (as that would break the level for others), but we have the password, and that's all we need!
Another day, another git level
Okay, so this is the "UPPERCASE SHELL". What does it do?
Well, apparently (and not surprisingly), it turns everything we enter to uppercase before it gets parsed. As most commands in Linux are lowercase, we can't really do much here. So we need to find a way to get around that.
Some fuzzing shows us that the input is probably taken, made into uppercase and then passed on to an sh command. Valid parameters for sh like -C are filtered out.
In bash, $n (where "n" represents a number), refers to the nth argument of a command call. $1 would be the first parameter, $2 the second, and so forth. $0 refers to the command itself. As we figured out above that our input is passed on to an sh call, $0 will be referencing sh itself, starting a new shell (without the UPPERCASE mechanics).
We have a working shell! Now let's just grab the password and get on with it:
There we have it!
According to the Level 33 -> Level 34 page, this is it. We've done it! Here, have a victory celebration song:
https://www.youtube.com/watch?v=D-mUjKDbDfY