In March of 2012, the folks over at Stripe (stripe.com) hosted a capture the flag wargame in a fashion similar to the smash the stack wargames. Unfortunately, I didn’t find out about the Stripe CTF event until after the fact, but they were kind enough to post the levels and source code (https://stripe.com/blog/capture-the-flag-wrap-up). So what I chose to do was set up my own server with the different levels and simulate the wargame as best I could (it appears they may have updated the initial post to download the server images they used, so you could go with that option as well).
It took me a little bit of time to set up the different users and get the suid permissions set correctly for all of the levels, but it was fun. I also thought this might be a good experience in order to set up a similar environment for some of the security classes I help teach. But for now, I didn’t spend any further time to secure the distro than simply getting the levels set up, so naturally I’m not going to be hosting my CTF server publicly as there are definitely other ways to own the server rather than just seeking the flags.
This series of posts walks through each of the levels. Keep in mind that my “flags” are different than the originals, since I set this up myself. So if you see other writeups online, that explains why my flags are different.
To begin the game you are given the password for the level01 user and you must login to the server via ssh. Once logged in, your goal is to obtain the password for level02. All passwords are stored in the /home/<level>/.password files. All exercises are found in the /levels directory.
Alright, let’s get started. I set the password for level01 to something simple to remember and connect to my server with the appropriate credentials. We’re presented with a basic introduction message.
Browsing to the /levels directory and doing a ls -al to see the directory listing, we notice a series of different binaries owned by the different user levels of the game. Also notice that their suid bit is set. This is an important part of the wargame, and most wargames of this fashion. We see that the level01 binary is owned by level02, so if we can somehow exploit the binary we can conceivably recover the password for level02. We also see that the source code for the level appears to have been provided in level01.c.
Your next step might subtly indicate something about your “security personality”. Do you execute the binary first or look at the source code first? Typically I like to look at the source code first, if I can. Even though this is a simulation and technically you’re executing the binary on a server (as opposed to your local system), I still like to have an idea of what a program is going to do before I execute it. So let’s view the source code.
It appears to be a fairly small C program that doesn’t take any command line arguments and simply prints the current system time. Let’s execute the binary to validate that it does what it says it does.
Yep, pretty straightforward. Okay, so where would the vulnerabilities in this simple 10-line program exist? It doesn’t take any command line arguments, so buffer overflow or format string exploits seem to be out. Let’s look at what the system() function actually does. If we type man system we see that it actually executes a shell command using /bin/sh.
Bingo, we’ve found the vulnerability! Notice that the program executes the date shell command via system(“date”). This is equivalent of executing date directly from the command line. The vulnerability lies in the fact that the program does not specify the full path when attempting to execute the date command. Thus the program is going to rely on the $PATH environment variable of the user executing the program in order to determine where to find the date executable. You can always determine the actual binary to be executed when issuing a command without it’s full path by using the which command. For example if we type which date, we see that the current date command that is in our path is /bin/date.
Since they don’t specify a full path to the intended binary, we can manipulate our path and create another executable file called date. Then our version of date will be executed and not the intended /bin/date.
We have write access to /home/level01. So let’s create an executable file named date that will print the contents of /home/level02/.password to the screen. To do this issue the following command:
echo “cat /home/level02/.password” > /home/level01/date
Verify that the contents of /home/level01/date appear correct, and then change permissions on the file to be executable via this command:
chmod +x /home/level01/date
This entire command sequence is below.
Okay, so our special date program is setup correctly. Now we need to add it to our path so that it will be executed when running the level01 binary. We do this by adjusting our $PATH environment variable to search our home directory first before looking in other places for the date program. You can either set your path to only search the home directory or concatenate the home directory to the existing path. The key is ensuring that /home/level01/date is the first instance of any date program found. The following command prepends /home/level01 to our path:
You can verify that our date program is now in our path by issuing the which date command again.
We’re now ready to reveal the contents of the password for level02. Execute /levels/level01 and notice how our password (in my case an md5 hash) is displayed instead of the system time.
Sweet! On to level02…
This was a fairly simple exercise and gets our feet wet with the wargame environment. It’s important to remember all of the items under your control when looking for vulnerabilities in binaries, regardless of how small they are. You might want to save the passwords for each level, just in case you forget and don’t feel like repeating previous levels to discover the passwords again.