The vulnerability is a combination of Apache v.2.3.9's default setting to not read .htaccess files and my mistake of relying on .htaccess to enforce security of the sample PHP upload component.
To give you some context on how this could happen:
- As the project name implies, this started as a client-side jQuery plugin, with a dummy PHP script to echo out the uploaded file
- Over time, I added a couple of sample server-side upload components, including two for Google App Engine (Python + Golang) - which I used for the demo - and one for PHP, which I never used myself in production
- I used the PHP component for local tests with various possible file uploads, including very large files and chunked uploads, which required enabling all file types for upload. My thinking was that allowing all file types for upload is not critical as long as the handling of those files is properly configured.
- Prior to adding the .htaccess file, I mistakenly assumed developers would configure their Apache server themselves so that no PHP scripts would be executed in the uploads folder. It was only added in this commit: https://github.com/blueimp/jQuery-File-Upload/commit/13931c7...
- The Apache servers I tested with always had support for .htaccess enabled, so I never bothered to check that the default Apache configuration since version 2.3.9 actually disabled it
- Never assume people actually read information about security
- Never rely on .htaccess for security configurations in Apache
- Make sure that published code is secure in all default configurations
- Never allow all file types for upload by default, even if it is secure in your configuration
- Recommend users to not upload files in the same root as their executable web application
- Always follow security best practices, even if it makes setup for users more difficult
I wanted to make it really simple for users to install a generic and secure file upload service with a great user interface.
Unfortunately, security best practices and ease-of-use are often at odds to each other.
I really don't blame the author here, sure there was an issue with the sample code - but come on it was sample code. If someone is implementing user uploads they should really do the due diligence and understand what the sample code does.
To be honest I'm not really that surprised that the vulnerability stayed hidden for so long; many PHP users are hobbyists or come from a more traditional "webmaster" background. This is not to say that there aren't good PHP programmers, just that there is a large group of novices.
Not a good way to look at it. Sample code is equivalent to production code. It will be copied, verbatim, if it appears to work. Once it has the appearance of working, it isn’t looked at again. It doesn’t matter if you think people should be doing this or not. The only thing that matters is they will.
It wasn't exactly hidden seeing as how there was a YouTube video titled 'Exploit jQuery File Upload Vulnerability' available since 2015. I don't blame the author (given the timing of the Apache change it probably would have been easy for him to overlook[1]) and it is surprising that this took years for anyone to make him aware of the issue since the exploit wasn't exactly unknown. Apparently there's some groundbreaking work left to be done in infosec searching on combinations of various library / application names and 'exploit'...
[1] I assume that like most of the rest of us he was lagging behind the latest and greatest Apache release a bit. So when he was writing/testing this, it probably wouldn't have been an issue.
Unfortunately, I never tested it with an Apache configuration that had .htaccess support disabled and so it simply did not occur to me that the default was "off".
I think the bigger issue was that the PHP sample code allowed all file types by default - this would not only affect Apache, but any Webserver that had broad rules to execute PHP scripts found in a directory.
Originally I didn't see this as an issue as I trusted developers to securely configure their server to make sure no uploaded files would be executed, which is why the .htaccess security settings were only added later in this commit: https://github.com/blueimp/jQuery-File-Upload/commit/13931c7...
But neither was the documentation informing developers clearly enough about the security implications, nor should I have relied on people actually reading security notices.
I'm not going to throw stones either. But I would encourage developers to make their sample code robust. Especially as
> many PHP users are hobbyists or come from a more traditional "webmaster" background
I seem to remember a somewhat related problem with WordPress. The issue there was made worse by the fact the sample code was part of the default install and so available to anyone who knew the URL.
Edit: A quick search shows I was thinking of an XSS attack. It was due to some bad example code in the default theme folder, which is by default publicly accessible even if not used.
I agree, the sample code should have been secure by default (with all web server configurations) because it's guaranteed that someone will use it as is without checking their own server configuration.
And inexperienced webmasters were definitely part of the target group, since I wanted to make is as accessible as possible, including for those users on shared hosting webspace without access to the Apache configuration files.
I do think that I share at least part of the blame.
Enabling all file types by default was not necessary and would have prevented this issue.
Especially since there are so many inexperienced developers using PHP, the defaults should have been secure in every perceivable Webserver configuration.
Understanding what the code does is not how people generally write software. They slap components together, copy&paste examples and randomly move code lines around until it seems to work good enough. So your examples better be correct :)
> The Apache servers I tested with always had support for .htaccess enabled
For what it's worth, you'll find lots of people disable support for .htaccess for performance reasons.
It's probably less of an issue these days, but if you support .htaccess files, apache reads the .htaccess file for the current directory, and those for every parent directory, and it happens on every single access.
So for example someone accessing http://foo.bar.com/thing/goes/here would cause the webserver to check for and read /thing/goes/.htaccess, /thing/.htaccess and /.htaccess. If you imagine you've done the sensible thing and got your content nicely split out, you can probably see how that can hurt performance. *nix OSs will be smart and cache the content etc. but you're still wasting time on every access making sys calls.
If you put the access rules in the apache config file, everything is set-up at initialisation time and it never has to make further checks.
Agreed - in addition to performance issues, there are also security issues if .htaccess support is enabled - because they can override security settings.
Originally the PHP example code didn't have a .htaccess file - I trusted developers that they would configure file uploads securely by themselves, e.g. via Apache configuration files.
The reason I added it was to support developers on shared hosting plans without access to the Apache configuration - something which was very common back then.
"Zero-day in popular jQuery plugin" is a bit misleading for the title then. More like "Zero-day in copy pasted code in PHP if apache incorrectly configured".
I think the title is substantially misleading in that it suggests the bug is in the plugin itself. Maybe "Zero-day in PHP server sample code provided with popular jQuery plugin"?
> Never allow all file types for upload by default, even if it is secure in your configuration
The general form - never use default-allow - should be used in most situations where set of possible inputs cannot be exhaustively enumerated. In most situations, you cannot enumerate badness[1]. Instead of trying to define valid input by specifying it's inverse, it's far simpler (and safer) to use default-deny and specify valid input directly.
Although I agree with you in a general sense, in this case there was no blacklist mistakenly used.
If the server is configured to serve uploaded files securely, it is feasible to allow all file types for upload (e.g. think of Amazon S3 or Google Cloud Storage).
However since there is no way to ensure that the server security settings to securely handle uploaded files are applied, limiting file uploads with a whitelist minimizes the attack vector sufficiently.
Since this is about vulnerabilities in a third-party dependency (ImageMagick/Ghostscript), the recommendation in the blog post to use the GD library instead (what the image_library 0 setting does) is not very sound, as libgd also had a number of vulnerabilities in the past, albeit less than ImageMagick:
https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=Libgd
A better recommendation is to securely configure ImageMagick, or even better: to use a safer image processing library (e.g libvips or imageflow).
There is no blame on you. There is no way you can provide a configuration that will be secure on every server.
My recommendation: add a big warning message that there is at least one known security hole and many unknown ones in the server code and it is up to the just to secure their server properly.
Then fix the demo code but leave the warning there.
It's always up to the developers to check the system they build for security problems. They could use the best frameworks in the world and copy thoro reviewed example code. The end result might still have huge security problems.
OK. I'll be that guy. Is there nobody who is going to talk about Larry Cashdollar, the person who discovered this vulnerability and first reported it?
I'm honestly not sure how I'd be more impressed; if you told me this man actually uses this as his real name in his daily life, or by finding out that this is actually his birth name.
Isn't a JQuery Plugin something that executes on the client-side? If so then how can something on the client-side compromise the security of a server? Isn't the fault on the server-side?
Or is this a PHP server-plugin which if installed on a PHP server makes them insecure? But of course anything you install on server can make it insecure. No?
What other vulnerabilities did this backwards-incompatible Apache change cause? Probably many people rely on .htaccess, for example to disable access to non-public files or disable php execution on a DIY CMS file sharing area.
Sounds like the risk from this is not widely known. Probably the correct solution for Apache would have been to detect presence of now-ignored .htaccess files and signal an error.
I think one of the reasons nobody reported this earlier was that people simply assumed that .htaccess support was the default - Larry Cashdollar, the security researcher, also confirmed this: https://news.ycombinator.com/item?id=18271880
Thanks! Comments like yours are what keeps me motivated to continue contributing to open source software.
But although the title is somewhat click-bait, I still think this counts as a vulnerability in my project, since there is a possible combination of default Apache setting and default project files that is exploitable.
Another apache disaster, blueimp's plugin has nothing to do with it: it's common for script kiddies to try to upload php executables on php sites, and sometimes it works.
I do think that my project is responsible and not Apache, since I provided sample code that was not secure by default when used in a default Apache configuration as is.
However I wish Apache would have changed their default config in a way that would have signalled an error if an .htaccess file is present but not applied.
Can someone share some information on the reasoning behind httpd's default handling of htaccess files? How does ignoring a security feature that many relied on improve security?
I suspect it's so that a user can't accidentally make security worse by fiddling with security settings that they don't understand. That said, I might've considered turning any page that had the disabled .htaccess settings into a 500 response instead of just keeping on like everything is working fine.
They disabled per-folder .httaccess files for performance reasons. Also for security, as it allowed server owners to control the entire server via global configs and prevent badly coded CMSs and plugins from opening security holes and bypassing stronger server-wide rules.
Haven't seen this sample code, but in general letting your users set the filename of anything on your server is a bad plan.
If the upload script just generated a random filename (046359905445.bin), and then stored the mime type, users filename, other metadata, etc. in a database, that would be a much better design.
I agree with you that this would be the safer route.
For a production file upload service, file uploads should ideally stored in a specialized blob store, e.g. Amazon S3 or Google Cloud Storage.
However the PHP code was written as easy-to-use sample code and I did not want to introduce a database as dependency and keeping the sanitized filename plus extension keeps the meta information intact.
If I had provided better information about how to securely configure the Webserver to allow all file types for upload, using the original - but sanitized - filenames would not be an issue.
It's not 3 years old, we've been exploiting it when we were 14 yr old trying to find server to host warez content, and has nothing to do with the plugin itself: it's all about apache's mod_php configuration: does it allow execution of php files that are in the directory where users upload their avatar ? If yes, then they can try to upload a php script and execute it on the server.