Ben provided some details morning on what happened to his server and I thought I'd follow that up with some precise details on how Galleon was hacked. As an open source developer whose work is used by many organizations, I take security risks with my software very seriously. When this hack was pointed out to me (more on that in a second), I got a quick fix up within about 20 minutes. I then followed that up with a more detailed fix later.
I want to personally apologize to anyone who may have impacted by this, and certainly extend my deepest regrets to Ben and Michael. I tried my best to correct this quickly, but at the same time, I didn't want to spell out the details until folks had a few days to patch up. Hence my twittering and vague blog post a few days ago.
Credit for this find goes to the engineers at Mura. Not only do they have a great product, they have great minds as well. They approached me personally with this before going public themselves, and I appreciate them giving me some time to fix things.
Again - folks - I take this very, very seriously. Please come to me if you think you see a security problem with any of my code. I can call myself a Jedi all I want, but at the end of the day I'm imperfect and need help from my users. Ok, so enough preamble, here are the details.
Galleon has two places where folks can upload files: avatars and message attachments. For both, the process was: Upload and then check the extension. Avatars were checked for image attachments and message attachments were checked against a list (defaulting to things like .txt, .pdf, etc).
So if you tried to be sneaky and upset a CFM file, ColdFusion would recognize this and then delete the file. However, all uploads were done to the proper folder for each time. So uploading a CFM for your Avatar put the file into the images/avatar folder. It would then check the extension and delete it. This wasn't instant, even though it may have looked liked it. There may have been 1-5 MS between the time the uploaded file was copied into the proper folder and the time it was checked.
The attacker used a load tester. A load tester is normally used to drive a lot of HTTP traffic to your site to see how well it responds. At a basic level though it's like someone sitting there with his browser and hitting reload really, really fast.
So if the attacker had a file named sss.cfm, he could begin the load tester driving traffic to galleoninstall/images/avatars/sss.cfm. He would then do the upload. His attack file was set up to first make copies so even though sss.cfm would be deleted, he would then be able to use the new file to perform other nefarious deeds.
So why did I upload files under web root? Galleon was meant to be installed under your web root itself. It was also meant to be installed easily by everyone, including folks using shared hosting where you - normally - don't get access to folders out of the web root. That was my thinking about why I needed to do the uploads like that. But I was wrong. It was easy enough to change the upload process to make use of getTempDirectory(). This is what I do now. The file is uploading into the temporary directory, checked, and then moved to the final destination if it passes the extension test. I also went the extra step and renamed the file to a UUID. My thinking here was that no one really cares what their Avatar file names are nor do they care about attachment file names since I can force the download to use the nicer, original name when you download.
I hope my screw up here helps others. If you thought uploading to a web root folder is ok because you "immediately" delete it, please remember that immediately on a machine isn't quite what you may think it is.
Archived Comments
Good post. I think we can all learn for mistakes like this.
Good job Ray.
when you first posted about this whole file upload security problem back in june, i took the liberty of coming up with a udf to hopefully address this. i finally moved the udf from a gist to an actual github repo and i would love if people could look over the code and contribute to the project.
http://github.com/rip747/cf...
I love the idea of both using a temp directory, and renaming the file to a UUID. I think your reasoning that no one cares what those files are named makes perfect sense.
Interesting post, Ray. Some good ideas.
Incidentally, one thing I have done in the past is:
If I have a directory that I know should not execute any files, I put a one line Application.cfm file in it. The Application.cfm has a cfabort in it, and that is all (or a cflog and then cfabort). This prevents any cfm files from executing in the directory. Just a little idea.
Thank you for your explanation here...I better go look at a few of my projects that allow public uploads and do the same.
@Neil: Actually, that is exactly what I did in my first patch. Except I used cflocation. I added those files to every folder where a CFM existed or where uploads could occur. I still have them there even with the 'better' fix in place.
Can't be too anal about security.
We use a low-tech solution in places where we need to hold uploadable files in the web root:
<CFIF ListLast(GetDirectoryFromPath(GetTemplatePath()), "\") IS "user_upload">
<CFLOCATION URL="http://www.#siteAddress#" ADDTOKEN="No">
</CFIF>
We could just as easily bounced them to "http://" oblivion, but we just send them back to the home page if they try to directly access anything in the upload folder.
Far from comprehensive, and not sure if it would have helped here, but it generally helps prevent shenanigans from those folders.
One thing to remember about the App.cfm solution - it only works with CFMs. If a hacker can get a PHP/ASP/JSP page up, and your server supports those, then this solution is worthless. That's why my app.cfm solution was just the first draft.
It's things like this that make me wish ColdFusion would supply the filename before you even consider going near CfFile[action=upload]. PHP gives you the $_FILES global with a lot of file information in it (including the original filename) before you even consider using move_uploaded_file().
In our file upload manager, we have the <cffile action='upload' immediately save the file as a '[UUID].[odd extension]' within a directory that is located under the webroot. For example, if I were doing this in galleon it might look like:
<cffile action="upload" filefield="fileField" destination="#uploadPath##fileUUID#.gal" nameconflict="overwrite" result="fileInfo" />
We then store the new file name and the old file name (client file name from the upload result) and then serve up the orginal file name when the file is requested. We have an allowed file extension list as well that checks the original file name upon upload and then removes it if it is not in the list.
So, as I save the file as a UUID and different extension in the cffile upload, is it still susceptible to this same attack? Unfortunetly I do not know enough about the mechanics behind the <cffile upload process to tell.
I am more than likely going to add the blank application.cfm with the cfabort in it as an additional precaution (as Ray said, can't ever be too safe), but I would still be curious to know if renaming the extension within the upload is a good preventative.
Good work! Was trying out Galleon on my laptop, and was going to install it at my CF site, but when I saw your tweets, I decided to hold off a bit until you were ready to give more details. Thanks for doing so . . .
Just a reminder folks, that I have a list of security tips for uploading files in ColdFusion: http://www.petefreitag.com/...
I pointed out the potential for this issue in that article, I said there was a "a slight chance that I could execute that file before you can delete it if you uploaded it into the web root (and I could predict where it would be placed)."
After writing that article I did some testing with this method, and I found that a "slight chance" is probably not accurate, it did not take a great amount of load to accomplish this.
Thanks for the writeup about this Ray.
Using this technique all web application languages could be vulnerable. Now if we could only start collaborating on info on the hacker (IP & other CGI data) we could find a couple of these #$%holes and hang them out to dry. I bet the majority of them are at home and not in an internet cafe.
Thanks for the heads up! I hadn't considered this possible security exploit before. It's a very creative attack, but fortunately the fix is pretty easy.
Let me just triple "ditto" Pete's URL. If folks have not yet read it, please do. (And when I catch up today I need to ensure it is hotlinked from my own security guide.)
i've noticed a profiles.cfm file in one of my galleon installation's avatars folder.... it looks dodgy... what's the best way to fix this?
@sean: Um... delete it?
Thanks for the reply Ray, I guess it sounded like a dumb question. I have already deleted the file... but after looking at that code... it's obviously executing a whole load of stuff, I was more wondering if there is anything that this code creates, deletes, compromises, anything at all that you know about that I should be looking at?
Well I know what Galleon's profile.cfm does, but not the file you speak of. After deleting it, be sure to update Galleon and it should not happen again.
Hey Ray, your article is a good reminder and your candidness about the matter rocks. Thanks.
Nice. I also load files above the web root and move them under the web root if they pass security. I've extended this to provide CFC security by putting them above the web root too. No one can introspect the CFC that way.
Typically people can only introspect the CFC if you leave the CF Admin/RDS password blank - which I hope you never do. Of course, people _can_ see your remote methods if they parse the WSDL. Then again, I'd rather shove rusty nails in my eyes than read WSDL. WSDL is XML done by the Marquis de Sade.
very true on both. Still, not being able to wsdl is just another tool in the security arsenal.
Yup. I was looking for something else when I stumbled on this. It is an age old rule of programming to always know what is coming in before letting it loose inside your program. Classic is the buffer overflow attack or the SQL Injection. But this goes right in there. Quarantine a file until is verified before putting it where you really want it. Renaming a file as soon as it comes in is good. One other thing I've done is use a variable to store IP and then limit how often they can access the upload form. Of course the App.cfc that redirects browsers away from the folder needs to be in there too.