How Galleon was Hacked

This post is more than 2 years old.

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.

Raymond Camden's Picture

About Raymond Camden

Raymond is a senior developer evangelist for Adobe. He focuses on document services, JavaScript, and enterprise cat demos. If you like this article, please consider visiting my Amazon Wishlist or donating via PayPal to show your support. You can even buy me a coffee!

Lafayette, LA

Archived Comments

Comment 1 by Dan G. Switzer, II posted on 9/21/2009 at 4:17 PM

Good post. I think we can all learn for mistakes like this.

Comment 2 by Phillip Senn posted on 9/21/2009 at 4:38 PM

Good job Ray.

Comment 3 by tony petruzzi posted on 9/21/2009 at 5:24 PM

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.

Comment 4 by andy matthews posted on 9/21/2009 at 5:34 PM

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.

Comment 5 by Neil Moncur posted on 9/21/2009 at 5:56 PM

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.

Comment 6 by Harry posted on 9/21/2009 at 5:57 PM

Thank you for your explanation here...I better go look at a few of my projects that allow public uploads and do the same.

Comment 7 by Raymond Camden posted on 9/21/2009 at 5:59 PM

@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.

Comment 8 by Paul Dynan posted on 9/21/2009 at 6:09 PM

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">

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.

Comment 9 by Raymond Camden posted on 9/21/2009 at 6:30 PM

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.

Comment 10 by David Boyer posted on 9/21/2009 at 6:53 PM

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().

Comment 11 by Daniel Budde posted on 9/21/2009 at 6:53 PM

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="" 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.

Comment 12 by Lola LB posted on 9/21/2009 at 7:03 PM

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 . . .

Comment 13 by Pete Freitag posted on 9/21/2009 at 7:07 PM

Just a reminder folks, that I have a list of security tips for uploading files in ColdFusion:

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.

Comment 14 by Simon posted on 9/21/2009 at 7:10 PM

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.

Comment 15 by Richard Davies posted on 9/21/2009 at 7:27 PM

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.

Comment 16 by Raymond Camden posted on 9/21/2009 at 8:59 PM

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.)

Comment 17 by Sean posted on 9/22/2009 at 3:13 AM

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?

Comment 18 by Raymond Camden posted on 9/22/2009 at 3:22 AM

@sean: Um... delete it?

Comment 19 by Sean posted on 9/22/2009 at 3:30 AM

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?

Comment 20 by Raymond Camden posted on 9/22/2009 at 4:01 AM

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.

Comment 21 by Joshua Curtiss posted on 9/22/2009 at 5:20 AM

Hey Ray, your article is a good reminder and your candidness about the matter rocks. Thanks.

Comment 22 by Warren Koch posted on 9/22/2009 at 11:04 PM

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.

Comment 23 by Raymond Camden posted on 9/22/2009 at 11:06 PM

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.

Comment 24 by Warren Koch posted on 9/22/2009 at 11:31 PM

very true on both. Still, not being able to wsdl is just another tool in the security arsenal.

Comment 25 by Don posted on 9/25/2009 at 1:03 AM

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.