[.net] .NET Image from stream vulnerability.

Started by
8 comments, last by mutex 16 years, 11 months ago
I have decided to allow users to upload images to my forum (something I've procrastinated on, because I feared for my forum's security), but I cannot get a secure way to do so. My concern is that validating that the stream is a bitmap is not sufficient; a bitmap's dimensions need to be considered and compared with the size of the stream. To test my concerns, I crafted a 5 KiB 24bit Bitmap and loaded it into the .NET Framework's Bitmap functions, and surprisingly it was vulnerable. Screenshot of me making a 5KiB bitmap that requires 96GiB of RAM to allocate. Screenshot of some attempts to get C# not to try and allocate 96GiB of RAM for this obviously forged bitmap. Other than writing my own header parser for all the graphics formats that I wanted to support (all the ones Bitmap.Imageformat supports), are there any solutions to this?
Advertisement


That's quite a nifty hack, but is it really that dangerous?

If it doesn't pose any other threats other than the allocation, a quick (but reliable) fix would be to just limit the memory usage of the upload application to make sure only valid (and sufficiently small) images are stored. I don't have much experience with .NET webapplications, but I guess this can just be set in the web.config file?
Rim van Wersch [ MDXInfo ] [ XNAInfo ] [ YouTube ] - Do yourself a favor and bookmark this excellent free online D3D/shader book!
Quote:Original post by remigius


That's quite a nifty hack, but is it really that dangerous?

If it doesn't pose any other threats other than the allocation, a quick (but reliable) fix would be to just limit the memory usage of the upload application to make sure only valid (and sufficiently small) images are stored. I don't have much experience with .NET webapplications, but I guess this can just be set in the web.config file?


yeah, max upload size is set in web.config, but thanks to an APS.NET Machine bug, handling it is rather difficult. If a user tries to post a file that is too large, it will throw an Application level exception that for some reason cannot be caught with the Application_OnError event handler in Global.asax. Your only recourse is to write a custom HTTP handler to handle uploading the file, but luckily there are examples of that readily available on the Web, as it is a relatively common problem.

[Formerly "capn_midnight". See some of my projects. Find me on twitter tumblr G+ Github.]

Quote:Original post by capn_midnight
Quote:Original post by remigius


That's quite a nifty hack, but is it really that dangerous?

If it doesn't pose any other threats other than the allocation, a quick (but reliable) fix would be to just limit the memory usage of the upload application to make sure only valid (and sufficiently small) images are stored. I don't have much experience with .NET webapplications, but I guess this can just be set in the web.config file?


yeah, max upload size is set in web.config, but thanks to an APS.NET Machine bug, handling it is rather difficult. If a user tries to post a file that is too large, it will throw an Application level exception that for some reason cannot be caught with the Application_OnError event handler in Global.asax. Your only recourse is to write a custom HTTP handler to handle uploading the file, but luckily there are examples of that readily available on the Web, as it is a relatively common problem.


Only images are going to be stored, so trying to limit the file_size of an image could be done relatively easy by putting restrictions on the dimensions of the image. And perhaps reading in the length of the stream? The image would first have to be delivered to the server for the earlier (if the later wasn't used), but considering there are equally easy ways to consume the bandwidth of a web-server, this should be sufficient. IIRC only BMPs support the ability to add multiple planes to the bitmap and such, but as I'd convert these BMPs to PNGs internally, it shouldn't pose much an issue.

Also, I'm going to presume that by "max upload size" you were appending to remigius's "valid (and sufficiently small)" suggestion, as I've already explained that the bitmap that requires 96GiB to allocate is only 5KiB in file size. Thus, "max upload size" would not solve said vulnerability, but it would prevent people from forging bitmaps that required a few bytes to allocate, but had a file size of a few hundred MiBs -- I have know idea why someone would do that though.
He isn't talking about the file size upload limit at all, read his post. He is talking about the fact that it is possible to fit a very large (pixel dimensions wise) image into a very small file. His example used a bitmap header, but it can be done just as easily with other formats (in fact it's possible to construct a 100% valid image file in only a few KB that when decompressed into memory will use up several GB).

His goal is that he wants to validate that a file uploaded is a bitmap, and not an exe or something else with the extension changed. To do that he is attempting to open the file to see if it's a valid image. The first stage of loading an image reads the images header, and allocates the memory for the decompressed image, which in this case instantly uses up several GB of memory or causes an out of memory exception.

He wants an easy way, without having to reinvent the wheel, to read just the image file header. To be honest, I've actually found it's not that hard to determine the basic attributes (format, width, height) of the popular image formats, and so I'd lean towards just going ahead and writing something custom to precheck the image dimensions.
Quote:Original post by Michalson
He isn't talking about the file size upload limit at all, read his post.


I did, now read mine [wink]

I'm not suggesting reducing the max upload file size, but limiting the maximum amount of RAM the upload process/application is allowed to allocate. As I mostly work in PHP for web stuff, this immediately came to mind as PHP is -by default- limited to a maximum of 8MB per process (when using it as a CGI executable). This allows it to reduce the impact of vulnerabilities like this and prevent faulty allocations and memory leaks from dragging the entire server down.

I don't know if ASP.NET allows specifying the max amount of RAM per process/page/thread/application, but since PHP can do it I figured .NET might also have an option for this. It may not solve the cause of the problem, but it mitigates much of the effect.
Rim van Wersch [ MDXInfo ] [ XNAInfo ] [ YouTube ] - Do yourself a favor and bookmark this excellent free online D3D/shader book!
From what I've seen (in a normal Windows Forms app, not asp.net, but System.Drawing & co is one library for both of them), it doesn't really allocate that much memory, I tried 65536x16 and even 16384x16384 'crafted' bitmaps and the C# app ran just fine (once I caught the outOfMemory exception).

So, IMHO, just limit the file size and then make sure you have your loading code in a try { .. } catch { }.

(interesting thinggy: on such crafted bitmap, even Paint and IrfanView say 'out of memory' whe trying to load such bitmaps, so I guess the source of OutOfMemory is somewhere in the GDI).
Q: How many programmers does it take to write a nice piece of software?A: MORE.
The bitmap loading is done via GDI+ and what has already been said, it's not the filesize that's the problem, as the header can specify what ever size by patching widths and heights, however, to the OP, how do you know it's trying to allocate that much space ? because as far as I'm aware you get an Out of memory exception if there's not enough memory or the files corrupt/not supported, so it might be a case of the exception being thrown by GDI+ when it's trying to access data outside that of the actual file size, but one can only guess.
Quote:Original post by cobru
From what I've seen (in a normal Windows Forms app, not asp.net, but System.Drawing & co is one library for both of them), it doesn't really allocate that much memory, I tried 65536x16 and even 16384x16384 'crafted' bitmaps and the C# app ran just fine (once I caught the outOfMemory exception).

So, IMHO, just limit the file size and then make sure you have your loading code in a try { .. } catch { }.

(interesting thinggy: on such crafted bitmap, even Paint and IrfanView say 'out of memory' whe trying to load such bitmaps, so I guess the source of OutOfMemory is somewhere in the GDI).


You got an 'Out of Memory' exception on a 65536x16 24-bit (24 MiB) bitmap? What program did you use to do the hex-editing, and did you take into consideration the bit-order?

I crafted a 4096x4096 24-bit (384MiB) bitmap and ran it through a few stress tests (to attempt to determine whether it is allocating the memory or not). I did not receive an 'Out of Memory' exception with this new bitmap, but it did not appear to be allocating the memory before throwing "A generic error occured in GDI+" exception.

I'm still not entirely convinced though, as I've only tampered with the width&height properties of the bitmap, and I still have to consider how Mono handles this. I noticed that both GIMP and MSPaint loaded my forged bitmap (with the dimensions specified). Paint.NET threw an exception, but that was expected considering it uses the .NET Framework.

Nevertheless, it looks like this isn't an issue afterall. [smile]
It's probably checking the bitmap dimensions against a sane value, and erroring out early.

As an aside, I wouldn't completely trust the accuracy of GDI+ errors; it tends to return out-of-memory or unrelated errors in a lot of cases (particularly around fonts).

This topic is closed to new replies.

Advertisement