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.
June 4th, 2007 at 7:28 pm
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.
June 4th, 2007 at 7:42 pm
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
June 4th, 2007 at 8:28 pm
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.
June 4th, 2007 at 9:01 pm
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
June 5th, 2007 at 12:38 am
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
June 5th, 2007 at 4:16 am
@christ1an: Maybe RSnake has got a very, very huge WLAN sniper rifle at home to break into your WLAN.
June 5th, 2007 at 4:19 am
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
June 5th, 2007 at 7:59 pm
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.
June 5th, 2007 at 8:00 pm
er, That emoticon was supposed to be tongue in cheek,lol
June 6th, 2007 at 7:44 am
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.
June 6th, 2007 at 10:46 am
Cool stuff.
I didn’t realize the GD functions are vulnerable to attacks.
Thanks for letting me know.
June 6th, 2007 at 2:26 pm
@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
June 8th, 2007 at 1:16 am
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 …
June 19th, 2007 at 6:07 am
Looks like somebody’s using this in the wild:
http://isc.sans.org/diary.html?storyid=2997
June 24th, 2007 at 2:16 am
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.
July 25th, 2007 at 3:25 am
php wont let that execute for sure…even the crapiest oldest version. have you tried what you have stated?
September 3rd, 2007 at 2:34 pm
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
September 25th, 2007 at 8:39 am
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
February 6th, 2008 at 12:52 pm
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.
March 6th, 2008 at 10:39 pm
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.
January 22nd, 2009 at 10:33 am
Would putting the following in .htaccess prevent execution of the script?
Options -ExecCGI
AddHandler cgi-script .php
Just curious. Newbie.
May 2nd, 2009 at 3:00 am
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 “
May 2nd, 2009 at 3:00 am
… needs a “? php” to inital php code for the parser.
August 21st, 2009 at 7:50 pm
will work, you do not HAVE to start with ‘
April 30th, 2010 at 1:52 am
@stanglwirt: that won’t find “”. Also, there are other threats, not just php (though it is most common). And “
April 30th, 2010 at 1:53 am
Bug ate my comment…
@stanglwirt: that won’t find “”. Also, there are other threats, not just php (though it is most common). And ‘
April 30th, 2010 at 1:54 am
Again…
…
April 30th, 2010 at 1:56 am
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.