Back to Basics: When allowing user uploads, don't allow uploads to execute code
I got an email from a reader who noticed some very odd errors happening in her web site's global error handler. Fortunately she's using ELMAH for error handling, which as you may know, is a JOY.
She was seeing:
Access to the path 'C:\Windows\security\database\secedit.sdb' is denied
Well, that's enough to make your heart skip a beat.
She looked around and found a file simply named "list.aspx" that she didn't recognize. The weird part was that this file was in the /uploads folder. That's where users can upload files with her particular CMS.
The list.aspx even has authors listed. Perhaps for their LinkedIn page?
Thanks Snailsor,FuYu,BloodSword,Cnqing,
Code by Bin
Make in China
I won't list the full list.aspx here, but rather call out some highlights of this clear malware.
It had a LOT of spaces in the opening of the file.
Meaning, they were assuming you wouldn't scroll down. Seriously. Oddly, though, it was spaces, not carriage returns. Note Line 23 never ends. It's SUPER long.
It pointed to a lot of (comparatively) unusual domains
It had links inside to things like
- www.rootkit.net.cn
- r57c99.com
Note that the second one actually serves malware and bad JavaScript, so avoid it.
It's a whole admin console for a bad guy to attack your computer
This file actually has a dropdown with "Please select a database" with values like (this is just a taste):
- Use master dbcc addextendedproc('sp_OACreate','odsole70.dll')
- select * from openrowset('microsoft.jet.oledb.4.0',';database=c:\windows\system32\ias\ias.mdb
- c:\bin.asp' backup database @b to disk=@t WITH DIFFERENTIAL,FORMAT;drop table [bin_cmd];
- Exec master.dbo.xp_cmdshell 'net user'
- EXEC sp_configure 'xp_cmdshell'
They're going for complete control of the system, and this file is just the start.
It serves JavaScript from elsewhere
This bad aspx file also tries to bring in some bad JS from the second domain above.
That JavaScript tries to bring in even worse JavaScript from another location via an indirection. I won't even list these bits for fear that I'll get blocked for serving it!
The root of all of this is: Don't let users upload and execute code.
A fix for arbitrary code execution in user upload folders
What was the fix? Well, certainly not allowing someone to upload a file with a .aspx or .php extension for one, but also to mark the entire uploads folder as not executable! Here is the updated web.config:
<location path="upload">
<system.webServer>
<handlers accessPolicy="Read" />
</system.webServer>
</location>
I'm not a security expert, but I'd love to hear from YOU, Dear Reader, and some of the crazy stuff you've discovered on systems you manage.
About Scott
Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.
About Newsletter
It is not totally dangerous but still embarrassing that one exposes the entire source code and history.
Thanks Scott.
AllowUploadSpecialFilesOnlyAttribute
AllowUploadSafeFilesAttribute
But I noticed that the default for the accessPolicy is meant to be "Read", should the fix be set to "NoRemoteExecute" instead?
Just trying to understand what the differences between "Read" (default) and "Read" (explicit), if any, when running under a path.
http://myserver.com/cms/query.asp?q=SELECT%20%*%20%FROM%20%TBL_USERS&loggedin=true
When i told the client my concerns, he pointed out that with that construct, he just had to maintain one file insted of hundreds. And as IE only showed the first couple of chars of an URL without scrolling and users where "too lazy" to take a look at the complete URL, that would not be a security issue.
Ah, and for login, he cleverly constructed a ASP page, that looped through all the users and dumped out a Javascript like that:
var loggedin = /*snip: get Loggedin var from QueryString;*/
if(!loggedin){
// Redirect to LoginFrm
}
function logIn(usr, pwd)
{
if(usr =='ClientA' && pwd =='MyPassword')
{
loggedIn = true;
}
if(usr=='ClientB' && pwd =='MyOtherPwd')
{
loggedIn = true;
}
/*snip: reload referer URL w/ QS "loggedIn" var attached*/
}
Asked why this was done this way, he responded: "DB Calls are inperformant. So i make one call, and dump out all information needed"
To cut things short, he was completely prone to advice, and insisted things to be done his way. I think "clever" was the word he used.
However, I forgot to check for the special characters. So the user could give a name like "..\OtherUser\filename.ext", and it would be written to "C:\MyCoolSiteFileStorage\User1\..\OtherUser\filename.ext", which equals to "C:\MyCoolSiteFileStorage\OtherUser\filename.ext".
Several steps up would have been even worse, though the site was run as default IIS user, so System32, Program Files, etc. were not writeable.
Luckily, I noticed the bug before anyone else did.
http://stackoverflow.com/questions/2061678/iis7-web-config-to-allow-only-static-file-handler-in-directory-uploads-of-webs
Only grant security permissions that are needed, especially to internet facing services. Execute permissions should be treated with extreme care.
It turned out the hosts file was edited but it took me forever to figure that out because of the huge amount of whitespace before the very bad lines at the end of the file. At first glance, everything looked fine! At that point i learned to look at the scrollbars when searching for funny business on a machine.
Dangerous code shown in the example can have a major impact including browser blocking (FireFox's "Reported Attack Site" and blacklisting by search engines. This snippet is worth real $$$ when it impacts such an attack has on the business side of things such as lost revenue.
I used to fight a PHP site running legacy code not written by myself with issues like this all the time. Management couldn't justify the cost of an upgrade or patching the code until I pointed out that we lost $XXXXX a day each time the attack was done because customers could not use the site to purchase the product. This made it real easy to justify a $2K upgrade.
Seriously though, improper upload handling, SQL injection, XSS, these are all things that have been around since the dawn of the web. I guess they'll be here until the dusk.
The access to confidential documents were filter by this querystring parameter:
confidential=true
Anyway, when travelling on the help pages of this web application we were able to find all server names with their root passwords!!!????????
The security page in the doc just said this single line: "the application is protected by Unix".
Security is not a concern for some...
So it's definitely not only for CMS you should be careful!
We adopted the web.config trick as a second line of defence. It seems to work well even for classic ASP sites.
<location path="upload" allowOverride="false">
<system.webServer>
<handlers accessPolicy="Read" />
</system.webServer>
</location>
This will prevent the sub folders to override the handler.
Upload types should be whitelisted as well. Media type testing can be a good strategy as well. Is the .png really an image file? If not don't allow the upload.
This prevents the user being able to browse to the file he has uploaded, and prevents hacks in the file name.
Storing in a blob or web inaccessible folder is a better approach.
dim input = txtusername.text
cmdSQL = "SELECT * FROM user_table WHERE username=" + txtusername.text
Often this approach is easier than trying to work with an external folder (esp in some 3rd party hosting environment).
Comments are closed.
Our site also has user uploads. We don't place them is a sub-folder under the site, but in another folder (e.g. D:\Uploads), so the IIS doesn't see these files. Instead, we have a controller that downloads the files (/Files/Download/23).