0

I noticed that PHP Imagick changes the IDAT chunks when processing PNGs. How exactly is this done? Is there a possibility to create IDAT chunks that remain unchanged? Is it possible to predict the outcome of Imagick?

Background information to this questions: I wondered whether the following code (part of a PHP file upload) can prevent hiding PHP code (e.g. webshells) in PNGs:

$image = new Imagick('uploaded_file.png');
$image->stripImage();
$image->writeImage('secure_file.png');

Comments are stripped out, so the only way to bypass this filter is hiding the PHP payload in the IDAT chunk(s). As described here, it is theoretically possible but Imagick somehow reinterprets this Image data even if I set Compression and CompressionQuality to the values I used to create the PNG. I also managed to create a PNG whose ZLIB header remained unchanged by Imagick, but the raw compressed image data didn't. The only PNGs where I got identical input and output are the ones which went through Imagick before. I also tried to find the reason for this in the source code, but couldn't locate it.

I'm aware of the fact that other checks are necessary to ensure the uploaded file is actually a PNG etc. and PHP code in PNGs is no problem if the server is configured properly, but for now I'm just interested in this issue.

user7390973
  • 63
  • 1
  • 6

2 Answers2

1

IDAT chunks can vary and still produce an identical image. The PNG spec unfortunately forces the IDAT chunks to form a single continuous data stream. What this means is that the data can be grouped/chunked differently, but when re-assembled into a single stream will be identical. Is the actual data different or is just the "chunking" changed? If the later, why does it matter if the image is identical? PNG is a lossless type of compression, stripping the metadata and even decompressing+recompressing an image shouldn't change any pixel values.

If you're comparing the compressed data and expecting it to be identical, it can be different and still yield an identical image. This is because FLATE compression uses an iterative process to find the best matches in previous data. The higher the "quality" number you give it, the more it will search for matches and shrink the output data size. With zlib, a level 9 deflate request will take a lot longer than the default and result in slightly smaller output data size.

So, please answer the following questions:

1) Are you trying to compare the compressed data before/after your strip operation to see if somehow the image changed? If so, then looking at the compressed data is not the way to do it.

2) If you want to strip metadata without any other aspect of the image file changing then you'll need to write the tool yourself. It's actually trivial to walk through PNG chunks and reassemble a new file while skipping the chunks you want to remove.

Answer my questions and I'll update my answer with more details...

BitBank
  • 8,500
  • 3
  • 28
  • 46
  • Thanks for your answer. 1) The strip operation itself does not change the IDAT chunks, it just removes tEXt chunks etc.. It is the Imagick constructor that flates the input file correctly (as the actual pixel data remain unchanged) but uses other parameters to deflate it again (to write the output file). I wanted to know which parameters are used here (e.g. which compression level, which history buffer (or window size) and which compression strategy) to generate a PNG which will have the exact same binary data (at least in the IDAT chunk) after being handled by Imagick. – user7390973 Jan 09 '17 at 13:00
  • If ImageMagick was used to create the original file, then you may have a chance. If another tool was used, you may not be able to generate an identical PNG file. If you're just stripping metadata, then decompressing and recompressing the image is a terrible waste of CPU time. Even worse that ImageMagick is, in general, a very slow set of functions. There is no way to access all of those parameters. IM uses zlib and a single parameter (compression level) to generate its flate data. – BitBank Jan 09 '17 at 13:09
  • 2) It is not my intention to find (or write) a tool which can do this, actually I'm looking for the opposite. I managed to do the exact same thing with GD, so I know it isn't "secure" in this point (though there have to be other vulnerabilities to use such a PNG in an exploit). But only the fact that I couldn't do the same thing with Imagick does not prove it's impossible. So I asked this question to clearify if it is theoretically possible or not. – user7390973 Jan 09 '17 at 13:18
  • Not sure what you're saying. What is GD and what are you trying to do? – BitBank Jan 09 '17 at 13:59
  • [GD](http://php.net/manual/en/book.image.php) is another image library for PHP. In [this](https://www.adamlogue.com/revisiting-xss-payloads-in-png-idat-chunks/) blogpost it is described how to generate malicious PNGs surviving the GD functions. So I am trying to do the same thing for Imagick (or "prove" that it's not possible). – user7390973 Jan 09 '17 at 16:59
0

I wondered whether the following code (part of a PHP file upload) can prevent hiding PHP code (e.g. webshells) in PNGs

You should never need to think about this. If you are worried about people hiding webshells in a file that is uploaded to your server, you are doing something wrong.

For example, serving those files through the PHP parser....which is the way a webshell could be invoked to attack a server.

From the Imagick readme file:

5) NEVER directly serve any files that have been uploaded by users directly through PHP, instead either serve them through the webserver, without invoking PHP, or use readfile to serve them within PHP.

readfile doesn't execute the file, it just sends it to the end-user without invoking it, and so completely prevents the type of attack you seem to be concerned about.

Danack
  • 24,939
  • 16
  • 90
  • 122
  • Yes I totally agree with you. This is what I wanted to imply with my last paragraph. Nevertheless I have a theoretical interest in this topic (practical applications could be CTFs and security testing). – user7390973 Jan 09 '17 at 16:50