6

As suggested on my StackOverflow question, I am now directing this towards the Information Security group, since no one was able to answer my question despite multiple up-votes.

User uploaded images account for a large portion of the content on the site I'm working on. I tried storing them outside of the webroot and fetching them with readfile() for security reasons, but it was just too slow so I had to go back to the old method.

Now I'm looking to make sure all uploads are 100% sanitized since they'll be stored inside the webroot. My question is, if a user were to rename a harmful script to a .jpg, .gif, .png, or .bmp and uploaded it, would it still be harmful when executed or fetched if the image was recreated with a function like this:

function imageCreateFromAny($filepath) { 
    $type = exif_imagetype($filepath); // [] if you don't have exif you could use getImageSize() 
    $allowedTypes = array( 
        1,  // [] gif 
        2,  // [] jpg 
        3,  // [] png 
        6   // [] bmp 
    ); 
    if (!in_array($type, $allowedTypes)) { 
        return false; 
    } 
    switch ($type) { 
        case 1 : 
            $im = imageCreateFromGif($filepath); 
        break; 
        case 2 : 
            $im = imageCreateFromJpeg($filepath); 
        break; 
        case 3 : 
            $im = imageCreateFromPng($filepath); 
        break; 
        case 6 : 
            $im = imageCreateFromBmp($filepath); 
        break; 
    }    
    return $im;  
} 

In other words, is there anyway to trick one of the imagecreatefrom* functions into executing content as a script instead of an image or would even a harmful script that's been run through this be reduced to a broken image?

Update I will also be checking file types and extensions, but I wanted to know if the user managed to bypass those, would this last resort help in protecting my server and/or clients in any way. Thank you.

DanL
  • 173
  • 7
  • Related questions [here](http://security.stackexchange.com/q/67058/3365) and [here](http://stackoverflow.com/q/21718688/1748148), although neither directly addresses the nuance of 'is imageCreateFrom* _itself_ vulnerable to malicious input attacks'. – gowenfawr Mar 07 '16 at 14:46
  • From reading the links in the comment by @gowenfawr it appears that `imagecreatefrompng` can relatively easily be compromised to contain PHP. So..... I think the perogative is to use `finfo` (*NOT* `exif_`) and confirm file is pure PNG (or whatever type) , although I'm not even sure that that helps, because it comes down to the fact that the image file is a genuine image file but contains some unwanted extras, and functions to check if a file is a genuine file can't realistically check for all possible unwanted extras, :-/ – Martin Mar 07 '16 at 15:37
  • Three words: double transcoding. – gowenfawr Mar 07 '16 at 15:38
  • your doubts are correct, `finfo` can also be bypassed (at least under certain circumstances, see the link in my answer). – tim Mar 07 '16 at 15:39
  • @Martin: try `cat evil.PHP >> image.PNG ; file image.PNG ; php image.PNG` – symcbean Mar 07 '16 at 19:54

3 Answers3

4

would even a harmful script that's been run through this be reduced to a broken image?

No, any harmful code in the image is still there.

But if it's secure really depends on what you do with the output of imageCreateFromAny.

On its own, imageCreateFromX doesn't do anything to the image. It doesn't recreate it, it just creates an image resource identifier, so if you want, you could further change the image. But any harmful code inside the image is preserved.

Your exif_imagetype check can easily be bypassed, so it doesn't ensure that the image doesn't contain harmful code.

So if you do not check the file extension, and you use your function eg like this: imagepng(imageCreateFromAny($imageName), $imageName);, then that would be insecure.

is there anyway to trick one of the imagecreatefrom* functions into executing content as a script

Lets hope not. That would not be a vulnerability in your code, but a vulnerability in PHP itself.

So what is the solution then? How can you make sure that the uploaded image doesn't contain any harmful code? Two ideas come to mind:

tim
  • 29,018
  • 7
  • 95
  • 119
  • If i'm understanding this correctly, converting an image to another type (from png to jpg etc) strips the meta data from the file, which would remove any harmful code (for both the client and the server?) Is this correct? If so, is there a way to strip meta data without converting file types? – DanL Mar 07 '16 at 19:14
  • @DanL Yes, changing the image type most likely will strip metadata (i guess it might depend on what method you use to convert, and from what to what you convert; some programs may convert the metadata as well). And yes, you can also strip metadata without converting. But metadata is really only one place where a payload may sit. png IDAT chunks are another (see links). So a good approach may be to 1) strip all metadata 2) convert to different type 3) search for the various PHP open tags. Of course, all this is in addition to file name extension filtering and proper directory permission. – tim Mar 07 '16 at 19:38
  • @DanL and if you really need the original image type, you could also convert back. If you eg convert png -> jpg -> png, it is unlikely that the original payload will still exist (it may not be impossible though, but this is only a second line of defense anyways) – tim Mar 07 '16 at 19:40
1

This method is only reducing the extent to which the image data from interacting with the PHP which is processing it - but the file interacting with something which has a much bigger attack surface the readfile() does.

Also validating the content is safe for your webserver, does not mean its safe for the client.

Storing them inside the webroot does pose additional risks - PHP's runtime will happily search any files sent to it for PHP code then execute it. The code you've added here does nothing to prevent that. PHP code gets into the PHP runtime via 2 routes - either as mapped by the webserver (normally based on the presence of a URL which resolves to a filename ending in '.php') or by include/require/eval/create_function etc within your PHP code.

You can add layers of protection against the first method by

  • not allowing uploaded files to have a .php extension
  • restricting uploads to a designated directory tree where the PHP handoff is disabled in the webserver
  • uploading the content to the document root of a seperate webserver which doesn't have PHP at all

Layers of defence for the second method is bit more tricky and expensive - code audits, suexec, disabling some php functions, locking down your opcode cache.

One useful tool, which you've already done half the hard work for in your code, and which provides protection for clients is to convert the file; if its a BMP or GIF convert to a PNG. For JPeg use webp (if your users can tolerate it) etc. While this won't be completely bulletproof it goes a long way to de-fanging attacks.

The code you've shown us only reads the content and checks it is a valid image format. Malware, including malware written in PHP can be mbedded in any of these file formats. Your code does not make any determination as to whether to accept content other than checking that the file format looks OK, nor does it show the content being modified in any way.

So as it stands, its not adding a lot of value.

symcbean
  • 18,278
  • 39
  • 73
0

I think there isn't anyway to execute a script through your images, but remember, the malware and new techniques attacks are discovered everyday, then, you have to implement different levels of defense. I recommend you the following:

  1. Implement validations: set a limit of size, a file name format and the type of extensions. The validations are very important, because it is the first point of attack, for example, null injection attack, someone can create a file named "script.sh%00img.bmp" and it could be executed on your server. Rememeber the validatios must be on side server.
  2. Implement anti-automation mechanisms, like a CAPTCHA. You could reduce the risk of denial of service attacks with this kind of security mechanisms.
  3. Hardening of the Web Server: set a isolated directory for your files, set the correct permissions, for example, don't set permissions of execution for your files, custom error messages and error pages and the actions when an error is presented, and more others settings.

I hope this information helps you.

Good luck.

hmrojas.p
  • 1,049
  • 1
  • 8
  • 16
  • 2
    While these are good ideas they're far more general than the question. I think elaborting on point 3 would be useful. – Martin Mar 07 '16 at 15:40