Acunetix
Paid Advertising
web application security lab

Passing Malicious PHP Through getimagesize()

I traded emails this afternoon with Michael Schramm who brought up an interesting issue where you can inject PHP through image functions that attempt to insure that images are safe by using the getimagesize() function. I’m not sure how often that is used alone, but I’m sure it happens. Here’s a snippet from the emails (edited only for readability and to re-link the images):

Yesterday, I’ve found out that it’s possible to include PHP-code in GIF-files which will still be recognized as a valid image by the PHP-function getimagesize().

If getimagesize() gives a positive Integer for the width, height, and type of the passed file, they just save it on their server with its original filename. Some webmasters are additionally checking the Content-Type of the file given in the HTTP-Header of the upload-request - but everybody knows that this is fakeable.

My basic file was a 8 by 8 pixels GIF-image (renamed to: something.php) which looks like this in a hex-editor: basicgif.jpg If you now insert some php-code into the payload of the image and call getimagesize('something.php'); it will give a valid result - but if you call something.php with a browser it will say something like “php error: illegal characters in input file” (I think this is because of the null-chars in the header of the image).

So I tried to insert /* in the GIF before the illegal chars to make php ignoring all chars behind this point. After a while I’ve got this file working: finalgif.jpg

This file passes the getimagesize()-function and executes the phpinfo() if called in a browser.

Sure, this issue is only dangerous if an image is only checked by getimagesize() and is saved with its original filename then, but there are many fools out there which do so!

Indeed! I’ve seen a lot of really strange ideas on how to secure uploads, and this is no doubt used in some places. Even still sometimes being able to get PHP into a system, even if it’s not named .php may provide some value if the attacker can execute local files but can’t include them remotely. This is an interesting follow on to yesterday’s post. I bet there is a lot of issues left to uncover with uploads.

28 Responses to “Passing Malicious PHP Through getimagesize()”

  1. yawnmoth Says:

    Who’s to say <?php echo ‘hello, world!’; ?> isn’t a valid sequence of bytes that can’t appear in a *.gif or a *.jpg? The palette of a *.gif, for example, can contain about 768 bytes (3 for the rgb color values * 256 possible colors) and there aren’t any restrictions on what those 768 bytes need to be.

    Personally, I think the best approach to getting rid of PHP code (or HTML, or whatever) in any file is to XOR that image with a “nonce”, of sorts. You upload a file, and an entry for that file is made in a database. One column could contain a randomly generated “nonce” and the other, the path to the file. You XOR the “nonce” with the file, which would effectively remove any “code” someone might have gone out of their way to add.

  2. Ronald van den Heetkamp Says:

    PHP image Injection for getimagesize() is known, fit was used for injection avatars in forums like PHPBB, I had to think about it but it rang a bell to me. This function has a bad history with multiple dos vulnerabilities and such. But, like Michael Schramm said: Plenty of fools who use it. ;)

    But as you probably know, checking on the filetype or ext. is also not enough due to possible null insertion to break possible filters like: /image.php%00.jpg

    There are good PHP classes to protect against this, or one could store the image as BLOB.

    Overall this is nasty stuff for webdevelopers, nice post :)

  3. devloop Says:

    Good news :)
    I wrote something about upload vulnerabilities some times ago.
    http://devloop.lyua.org/blog/index.php?2006/07/19/289-php-les-dangers-des-scripts-dupload
    I played with the dimensions of the GIF to get the smallest size but didn’t managed to get a valid GIF file for dispay and as it have been said PHP really don’t like null chars :(
    It’s a good way to solve it, I will give it a look tonight.

  4. christ1an Says:

    RSnake are you a spy or something? Few days ago, you wrote about Google and said they were not allowed to blubb…few hours before, I’ve pretty much used the same words in a conversation with Ryan Cartner. So when we read your post, we both thought hey, we’ve just talked about that :)

    Now, we’re doing some research on exactly this theme (File Upload Security, execution of PHP code) and Ryan discovered really interesting holes in most mechanisms.

    Anyway, just found it weird :D

  5. Lars Jankowfsky Says:

    RSnake,

    It is known for quite a while that you can execute any php code inside a picture. The most easy way todo this is simply add comments to a JPG. Validating the pic with getimagesize is senseless - as the pic has simply a comment inside which is valid.

    The real problem here is that the application must make sure that you cannont execute any files on the server - getting some code onto the server is quite easy. Even more easy than uploading a picture with PHP code inside is injecting the code into the server logs by setting your webbrowser to some php code. PHP simply parses the file and ignores all data until it comes to a

  6. Alex Says:

    @christ1an: Maybe RSnake has got a very, very huge WLAN sniper rifle at home to break into your WLAN. ;)

  7. mat Says:

    ive done this before, doesnt this method also reliy on the server, u can include code into a gif image and its valid and sometimes it wont execute the code, just the image

  8. Ryan Says:

    Hi.
    as Christ1an mentioned, we were discussing something very similar earlier today. This isnt the first time this has happened…

    I hereby charge that RSnake’s developed some really advanced analysis system in javascript that can develop hypotheses about events outside of the browsers context, probably monitoring all of our keystrokes at this very moment. It has been secretly embedded in his webpage using advanced obfuscation techniques that effectively cloak his script in what appears to be innocuous HTML and various harmless scripts. BE WARNED.

    :-?

  9. Ryan Says:

    er, That emoticon was supposed to be tongue in cheek,lol

  10. RSnake Says:

    Pfft, that’s low-tech. I went back in the past with my JavaScript time machine (don’t mock it, it was the most flexible language I could find at the time I wrote it - although it doesn’t work in all browsers) and after flying to your location I simply just shoulder surfed. Waaay more effective. ;)

  11. Del Says:

    Cool stuff.

    I didn’t realize the GD functions are vulnerable to attacks.

    Thanks for letting me know.

  12. Michael Schramm Says:

    @Lars Jankowfsky:

    You said “Validating the pic with getimagesize is senseless” - of course it is, I know that. But as I mentioned in my mail, there are a plenty of fools out there who do so.

    You also said “The real problem here is that the application must make sure that you cannont execute any files on the server”.
    Indeed! But the same fools who are validating images with getimagesize() are saving them with its original file extension.

    By the way: I cannot follow your idea of inserting php code into comments of jpeg images. I slightly remeber that comments on a jpeg are written into the file separated by null chars (on each char of a comment there’s a following null char) - if you remove them, the file won’t pass getimagesize(). And additionally, if you try to insert a /* before the first null char it also won’t pass.

    I know that this is a limited vector, but it works at a plenty of web apps :)

  13. bitchiller.de - Blog of the r0xx0r Says:

    Wie man PHP-Code durch getimagesize() schleust

    Vorgestern habe ich eine Möglichkeit gefunden wie man manipulierte GIF-Dateien, welche PHP-Code enthalten, durch die PHP-Funktion getimagesize() schleusen kann.
    Ich war zu faul einen eigenen Blogeintrag hierfür zu machen :), deswegen habe ich RSnake …

  14. Jordan Says:

    Looks like somebody’s using this in the wild:

    http://isc.sans.org/diary.html?storyid=2997

  15. Stefan Esser Says:

    I wonder what kind of “php” parser the original author tried…

    The PHP parser will usually eat ALL characters that do not belong to the PHP script and just output them. Including 0 bytes.

    You can “embedd” PHP code into any image by simply

    cat my.gif my.php > evil.gif
    php evil.gif
    [BUMM]

    And if you want to survive other thing just copy it into the palette of the gif.

  16. esteban Says:

    php wont let that execute for sure…even the crapiest oldest version. have you tried what you have stated?

  17. SasQ Says:

    The vulnerability isn’t in the getimagesize() function, but in the ability to feed the PHP parser by the uploaded file. getimagesize() doesn’t secure the upload method, because it checks only the header information, but the image pixel data could be anything - even a valid PHP code.

    Also the XOR technique mentioned earlier might not be enough if the hacker know the “key”. XOR is reversible, so the hacker may XOR his code with the “key” and upload XORed code, which after XORing again will make the unciphered PHP code again.

    If we want a good security, we should thing about what is the difference between an ordinary image and “executable” image? The difference is that the latter’s binary data could state a valid PHP code understandable by the PHP parser. So, we are able to recognize the malformed images simply by parsing it! :) If there are character sequences making valid PHP sections, and that sections contains valid, often used PHP code [something similar to function calls, variable dereferencing etc.], it’s no doubt a PHP code :-J

  18. Steve Says:

    Ok - here a question - what if you allow a user to upload a file - then just hit that file w/ straight HTML? Is it still vulnerable for exploit?

    Ie - if I have a test.html file that just has this in it :

    I allow the users to upload that file (named whatever they want) - I check that the extension is a .gif - then use move_uploaded_file to the /images/ dir.

    What type of venerabilities does that open me up for?

    Or is the exploit w/ an upload of whatever.gif.php then have php interpret that file?

    Or I guess more importantly what is the most secure way to do this?

    Thanks

  19. Josh Says:

    Converting the file to a jpeg (or png, etc.), resizing it, essentially making a minor change and rewriting the image data should effectively ’safe’ your image file.

  20. dan Says:

    don’t put any user submitted files into directories with executable permissions. that is, don’t ever put the image or video or file that the user uploaded onto a web-server directory that has the capability of executing the file.

    iis and apache have this capability, where you can turn off what can happen in the directory. essentially what you want to do is to make the target directory of uploaded content a safe zone, where the virtual directory that wraps that directory, has read-only, limited, and no execution permissions, other than just dumping the content to the browser as it is.

  21. thoughts Says:

    Would putting the following in .htaccess prevent execution of the script?

    Options -ExecCGI
    AddHandler cgi-script .php

    Just curious. Newbie.

  22. stanglwirt Says:

    maybe its possible to add a simple check into the app:

    $handle = fopen (”./test.gif”, “rb”);
    while (!feof($handle)) {
    $buffer = fgets($handle);
    }
    fclose ($handle);

    if(preg_match(”/php/”,$buffer)) echo “BAD GIF”;

    simply read the file and search for the string “php”. i think most php installations needs a “

  23. stanglwirt Says:

    … needs a “? php” to inital php code for the parser.

  24. Anonymous_ Says:

    will work, you do not HAVE to start with ‘

  25. Andrew Says:

    @stanglwirt: that won’t find “”. Also, there are other threats, not just php (though it is most common). And “

  26. Andrew Says:

    Bug ate my comment… :(

    @stanglwirt: that won’t find “”. Also, there are other threats, not just php (though it is most common). And ‘

  27. Andrew Says:

    Again… :(

  28. Andrew Says:

    What a crappy way of filtering comments… Don’t tell me you actually… ah, never mind. I wouldn’t expect that on ha.ckers.org.

    Here it goes again:

    “PHP start tag” might be a valid sequence in an image, so you can’t filter by it.

    To make Josh’s comment a bit more complete:
    - validate user supplied filename (replace all chars except [a-z0-9] with underscore)
    - validate file suffix (allow only image suffixes - jpg, jpeg, gif, png)
    - open and re-save image (to avoid IE “js inside image” bug)

    I think this procedure fixes all of the bugs / vulnerabilities I have found. YMMV.