The Final Bandit: more git, bash variables
Welcome back to the 13th iteration of this blog series. In this series, we’re growing our cybersecurity knowledge starting from the very basics using the overthewire.org challenges as a guide. First, I’d like to thank everyone for their feedback based on the last post! I’ll do my best to implement it and as always, more feedback is always welcome. Now let’s start!
Today is a special post because we’ll be finishing the last of the bandit challenges. We’ll pick up from bandit29, where we left off last week, and once again it’s a git challenge so let’s clone and see what’s in the “readme” file:

Sounds like a similar challenge to what we ran into earlier so let’s check the log for changes:

Just a username fix, but it seems that the password was removed from the file before the project released as a final product. Let’s check if there are any other branches for this project:

If we take a look at the list of branches, it looks very similar to a directory structure and in a way it is. git allows for branches of a project so a project can have different versions either as it updates or even for different functions. In this case we have a “production” branch and a “development” branch. So let’s checkout the “dev” branch:

Like a directory structure, switching branches essentially changes “directories” and we can see that there’s a new “code” directory in this branch as well as a completely different README.md file.
On to the next.
Seeing as how this is also a git challenge, we can just continue from the same bandit29 user. Once again we’ll clone the project and see what the “readme” has to offer:

This time there are no branches and no useful information in the “readme” and there were also no changes to it. There is one more utility that git provides and it’s very useful for taging information. Let’s see if there are any tags:

On to the next.
Another git challenge; let’s see what’s in the “readme”:

This time we’re going to update a project, so let’s start by creating the required file and adding it to the project:

Interesting:

The “.gitignore” is set to ignore any file name that ends in “.txt” so let’s remove it and try again:

Great, now we can commit:

It’s good form to explain our commits and changes, now to push our changes to the remote repository:

Awesome! The repository rejected our change, no doubt so that the challenge itself doesn’t change but it verified our file and gave us the password for the next level.
Last but not least, bandit32. The hint says we’re going to be stuck in some kind of shell problem. Let’s see if if we can figure out what it is before we get in:

So the default shell is not our expected /bin/bash, let’s see what kind of file this is:

So this is an executable owned by bandit33 but accessible to bandit32. I suspect we’ll be able to just read bandit33’s password once we figure out how to get a shell. Let’s try logging in now, this is about as much as we can gather since we don’t have access to the file:

It seems that this “uppershell” file converts all of our input into an uppercase string before passing it to regular shell. There aren’t any uppercase commands to use here so we’ll have to use something that stays the same regardless of case-changing: bash variables. If you recall from our previous script, bash calls variables using the “$” as a signal that we’re calling a variable. There are also some fixed variables that are always there unless changed: positional parameters.
Positional parameters are variables that depend on the executing command. They are always set by default but you can manually change them within a script and reassign them if necessary. Positional parameters start with $ since they’re variables and they start at “0” and increase to digit “n” so long as “n” is a single digit. There is also “$?”. $? is the variable for the exit code of a program:

As we can see, “$?” stores the exit code itself and as such sending it as a command to uppershell sends “0” to shell since it presumably executed its previous command and exited without problems. If a variable doesn’t hold any information, notice that it is empty and doesn’t get filled with a “0” simply because we called it:

Now back to what the $n variables store: command line arguments. Starting with $1, each of the variables store what argument you passed to the command that the variable is called from. For example, checking the $1 of “ls -al” would return “-al”. On the other hand if we passed the “-al” separately as “ls -a -l”, $1 would return -a and $2 would return -l.
Finally, $0 is special since it stores the name of the program itself. So $0 of ls is “ls”. Although the file we’re running is called “uppershell”, it’s simply passing our commands through an “upcase” function then passing it to “sh”. We can see that from the errors: sh is the one providing the errors. Therefore giving $0 to uppershell will simply pass $0 to sh which would start an instance of shell within uppershell. This instance is then running from within our original “$0” and therefore will bypass the “upcase” function of uppershell until we close the instance. Let’s try it:

Let’s go ahead and grab the password:

Perfect; with this, the last bandit challenge is solved and the last flag captured. Until they update the challenges anyhow.
Error handling is an important part of a program. It not only keeps things from crashing in a terrible and not-so-user-friendly way but also lets the developers know where the problem is and usually how to fix it. Similarly default variables such as the positional parameters can be very useful for a developer to interact with the user or the machine environment. While these are great in a development environment, sanitization is necessary in production. Detailed error messages can let an attacker know exactly what to do to satisfy the program’s need to function properly and still break out of its intended use. It is often why you’ll find a device produces error codes that require some index to decipher what the code means. In the same way, default variables might be used to gain unauthorized access by using the program in an unintended manner.
Instancing is an important part of both programming and securing applications. We’ve already encountered a form of instancing through set uid’s and when we used vim to gain shell access. The uppershell program we encountered was easy to analyze because of the detailed error messages and the way that it instanced the shell commands we passed.
An instance is can be seen as a process that runs independently from its true environment once started and assumes another environment given to it. In the case of set uid’s, the program ignores the true executer of the program and runs as the user defined but the set uid. In the case of vim running a shell and uppershell, they both execute a clean shell then wait for it to end before regaining control.
Since uppershell only attempted to change the case of our first interaction, we were able to execute a shell in an instance and force uppershell to wait for it to end before it can mess with our commands again. In fact, uppershell is not much different from bash, zsh, or dash. They were all just different implentations of shell that took our commands then passed it to shell. They then wait for that process to complete and exit before getting back their own control of the commands we pass to it.
I hope you’ve learned a lot from and enjoyed the journey through the bandit challenges as much as I have.