Roses are red,
violets are blue,
images contain code,
you're hacked.
I've just had the privilege to have my mind blown, while reading this article:
Encoding Web Shells in PNG IDAT chunks. All credits go to
Phil for this one, an IT consultant and hacker from down under. You should check out his other articles. Fascinating stuff.
In layman's terms, let's say you have a web page that allows user upload of images. You're feeling pretty safe about it, because when the image is uploaded, you immediately open and manipulate it in
GD, a PHP image library, before you save it on the server. If you can't open the file, it's not a valid image. That should destroy all
malicious code stored within the image, right?
That's right. It should.
But what if an image appears normal, and after you resize it in GD, a PHP shell appears out of thin air?
An image that contains a PHP shell "<?=$_GET[0]($_POST[1]);?>" when resized to 32x32 with GD.
There's no way around it. You can't really prevent it. And to make things worse, you're not even checking the file extension.
If you reverse the process of how the image is generated, you can encode all sorts of data in the image. When the image is then manipulated with GD, it produces plain text data within the image.
Sounds simple enough, but there are a few hoops one needs to jump through, to engineer such an image.
First, PHP code must be
compressed, then reverse the PNG
filtering process and finally embedding the data as raw pixels.
Depending on what the server does with this image, there are a few more tricks to be done.
If the file is resized with
imagecopyresampled(), the payload needs to be encoded in a series of rectangles or squares.
Et voilà! Your PHP shell.
Oh sh*t!
But what can I do about it?
Like I said, not much. Without getting in too much detail on how this sorcery is done (you can check the source article for that), all you can do is focus on the prerequisites that enable this hack to work. Just uploading the image is, thankfully, not enough.
If you're a developer, don't be stupid. Don't do stupid things. Validate user input. Sanitize data. Have total control over how and where the files get saved. Triple check file and directory permissions and file extensions. There are many image hosting services and with the cloud becoming more and more popular, there are things like
Amazon S3 you can use, to host data on third party.
Also, having total control over the file extension is not foolproof. You see, if your script contains a Local File Inclusion vulnerability (
LFI) as well as user image upload, then my friend, I have bad news for you.
An attacker can just as well exploit the LFI with the path to the .png on the server.
Oh, you have all your include()-s and require()-s prefixed and suffixed? Tell me all about it! But while you're at it, have a look at this
stackexchange debate.
[*sound of explosion*]
If you're a user innocently hosting your web page somewhere, you can hope that your hosting provider has tight security, but also check file and directory permissions. Anything that isn't specificaly meant for upload, shouldn't have write access for apache. That typically means 644 or
-rw-r--r--, for you. If you have
.htaccess enabled, you can disable PHP execution on directories with user upload. See how below. Make sure it's not writable by anyone else.
If you're a sysadmin, you can expect your users to run all kinds of outdated opensourcy mumbo jumbo, which is like magnets for abusers. But you can't just mess with their files and do as you please. You've g0t r00t, and that's your real power. Figure out which directories can be written to by the web server, and stop PHP execution on these directories.
For example, Wordpress:
<Directory /home/test/www/wordpress/wp-includes>
php_flag engine off
</Directory>
<Directory /home/test/www/wordpress/wp-content/uploads>
php_flag engine off
</Directory>
Even though these directories contain PHP scripts, they're never called directly by URL. They are
require()-d or
include()-d by
index.php originally. However, this raises another issue - source code disclosure, if done sloppy. The example above is quick and dirty. Yes, sloppy. Do some work.
Also, you can use a Web Application Firewall, like mod_security or things like that. Unfortunately, they wouldn't help in this my-png-is-a-shell situation. But they can solve a lot of other potential problems.
Well thanks, Jean! You've *really* helped me out with this information! ...NOT!
I've said it once, I've said it twice, I'll say it again.
There is no standard solution for this.
You're gonna have to find every hole through which an attacker can crawl through, fix every sensitive information disclosure (don't display error messages on the page, don't display source code - with php_flag engine off it will be displayed!). Don't be sloppy, don't be stupid.
Feeling safe yet?
If not, you can give us a call, and we can do some penetration testing for you.