CTF – PHP and OS Command Injection

This past weekend, SecureState’s technical consultants worked with representatives from the University of Mount Union to host a Capture the Flag competition for teams of local high school students.  The teams competed for scholarship money in challenges spread across six categories – Coding, Cryptography, Forensics, Grab Bag, Hacking, and Web.  The students’ collaboration, research, and diligent problem solving impressed all in attendance.  Though all the participants did very well, the clock ran out before they could solve all of the challenges.  This post kicks off a brief series in which we’ll address the creation of and solution to some of those outstanding challenges.

Hopefully its appeal extends beyond those who participated in the events to those who may be planning or participating in similar events.  The intent is that the posts might also be a good compliment to patchwork’s series on vulnerable virtual machines.


Creating the Challenge

This challenge required that the students exploit an OS command injection vulnerability to peruse the web server’s directory structure in an effort to find a key file.  Injection is the top vulnerability as rated by OWASP, and their site provides a good primer on OS Command Injection (as distinct from, for example, SQL injection).  Setting up a box with this sort of vulnerability is easy and allows the curious-minded to experiment with OS command injection from both perspectives – the developer and the attacker.

Provided readers have a decent Internet connection and means of virtualization, they could set up a similar vulnerable server in under 30 minutes.  Running through the process as I outline it below may take another 15 minutes or so.

I used the Turnkey Linux LAMP (Linux, Apache, MySQL, and PHP/Python/Perl) Web Stack for my vulnerable webpage.  The guided setup is straightforward, and required no change from default settings.  Once the server was up and running, I created a .php page (code provided at the bottom of the post) and placed it within the /var/www directory, the default location for hosted websites on many Linux distros.

Once the site is live, you can browse to it on your local network and start the challenge.


Solving the Challenge

As is often the case with injection, identifying the vulnerability in this example is much easier than exploiting it successfully.

The vulnerable website allows users to input a string and submit it to the webserver to generate and display the MD5 hash of that string.  The MD5 hash has nothing to do with the actual vulnerability, it merely provides the pretext for user-supplied input.  The challenge is for the attacker to (1) identify that the vulnerability exists and (2) exploit it successfully to find a file hidden within the directory.




Now it’s possible that our developer in this scenario created his or her own PHP code to generate an MD5 hash.  It’s more likely that he or she found and used preexisting code online.  But it’s also possible that he or she elected the most dangerous route and simply decided to pass it on to the underlying operating system.  One big hint here is the trailing “-“ following the hashed value.  If I Google “md5 hash followed by dash” I get a number of results that reference the Linux CLI md5sum program.  Furthermore, though I won’t go into them here, there are multiple tests or tools one could run to determine that this is an Apache webserver hosted on a Linux box.  In short, we have enough indicators to make testing for Linux OS command injection, at the very least, worth a shot.

In the OWASP command injection summary referenced above, the authors are able to execute another command simply by adding a semicolon to the end of their line.  In their case, both commands are executed without any complaint from the server.  Unfortunately, our case is not that simple.  If we add a semicolon and another command, the result is the first line being echoed back to the user, followed by a hashed string:




Our command is being executed successfully, but its output is being hashed – this is not very helpful, nor is it our desired end.  Since we added a command and received our first string back unaltered along with a hash, we might infer that there is an echo command preceding our input, and that the MD5 hash is calculated after our input.  It’s a lot to puzzle out, but it is a logical inference.

You could test for the presence of the “echo” command right on the site, by comparing the known hashed value of identical strings placed at different points in the field:






Notice how the hashed values are identical when preceded by the “echo” command, but totally different when not.  This helps confirm our suspicion that the command, as it’s passed to the OS from the PHP page, starts with “echo.”  We also know that it does accept the second command we include, it just hashes the output rather than the string.  Some research into using the Linux CLI to calculate MD5 hashes might be helpful.  A few more minutes with Google might leave us with the theory that our string is being hashed by the Linux CLI with a command that goes something like this:

echo <your string here> | md5sum

This would explain everything we have seen so far.  So if that’s the case and we want to see the directory listing, how would we do that?  Simply insert another command following the initial string!




Now what’s being passed to the underlying OS looks something like this:

echo test; ls; blah | md5sum

 (Note that in the actual challenge the directories had random names so that they couldn’t be easily guessed or brute-forced.)

It doesn’t even matter what our last string is, since by the time we get there we’ve already executed the command we wanted to.  Now that we have the working formula, we can look through the directories to find the elusive key file.  Keep in mind that our ability to use and execute commands will be constrained by the privileges assigned to our user, www-data:




For those wishing to create and complete this exercise at home, the complete .php page is gisted below: