Scott Hanselman

Back to Basics: When allowing user uploads, don't allow uploads to execute code

March 27, 2014 Comment on this post [37] Posted in Tools
Sponsored By

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


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">
<handlers accessPolicy="Read" />

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.

facebook twitter subscribe
About   Newsletter
Hosting By
Hosted in an Azure App Service
March 27, 2014 10:04
Would a framework like MVC need similar restriction of user uploads? In theory, a View would not get executed unless a Controller approved the request first.

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).
March 27, 2014 10:28
following the pattern convention over configuration, I would suggest a new folder app_upload, which is restriced by Default. Works quite fine for app_data.
March 27, 2014 10:31
My new favourite since yesterday is this: search for .git folder

It is not totally dangerous but still embarrassing that one exposes the entire source code and history.
March 27, 2014 10:39
For me I've would block any content type except the allowed only. But I didn't knew that there is such a think that I prevent the from executing a code from a certain folder.

Thanks Scott.
March 27, 2014 10:53
I've written 2 action fillers for ASP.NET MVC to allow upload special files only:
March 27, 2014 11:10
Awesome comments so far, folks! Very useful!
March 27, 2014 11:14
Never trust the client. Php uploaded as an image file discussed on stackoverflow.
March 27, 2014 12:16
Like Kobi, we also place all our uploads in file locations like "C:/Uploads/Website1/..". But never knew something like this could be done so easily, thank @scott for yet another educating post. Will spread this info with others too.
March 27, 2014 12:17
So just checking out this at the msdn, and this was introduced in IIS7.0 which means patching older sites won't be quite as easy.

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.
March 27, 2014 12:22
I once had i client, who proudly presented his current (self-written) cms. It was in these days around the Millenium - you know, when people who could spell HTML correctly were hired as "senior developers". I was fairly when i saw that the Url looked like this:*%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;*/

// 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.
March 27, 2014 13:35
I'd suggest even better than restricting execute on the folder would be to save the uploaded files outside of the web root. That way they can't be browsed either - you wouldn't really want people to be able to see even legitimate uploads from other users, particularly if they need moderation first.
March 27, 2014 13:41
I once wrote a site that allowed the user to upload data and specify the file name. Each user had a separate folder.
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.
March 27, 2014 14:26
You can enable only static file handler in upload folder:
March 27, 2014 14:41
This is another reason why you should not grant your web server process elevated permissions. When you give your IIS process higher or (please don't ever do this) admin level permissions, files can be written anywhere on disk and executed from anywhere on disk.

Only grant security permissions that are needed, especially to internet facing services. Execute permissions should be treated with extreme care.
March 27, 2014 14:45
A long time ago I checked the content of an upload folder for an intranet application that contained files for a file attachment feature. It contained everything from blurry 2-second videos to Window shortcuts. The allowed file types were restricted prompty after we saw that!
March 27, 2014 14:49
I know its not popular, but you could store the files as blobs in a database where they have no chance of being executed. It will be a little less performant, but it will be a lot easier to ensure you have it backed up properly. Make a page that streams out files based on the id of the file. You can also do much better management of permissions this way, and I'm not aware of a way to execute a blob in the DB that doesn't involve some kind of direct, non-web access(like ssh) to the server.
March 27, 2014 16:29
i had borrowed a co-worker's laptop while traveling a few years ago and the laptop would misbehave occasionally. i'd get these weird looking ads in the bottom-right corner of any browser :(

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.
March 27, 2014 16:55
It's so easy to forget the basics - thanks for raising the awareness. Seeing OWASP and other security measures - anti-forgery tokens, injection proofing often the items in your post are overlooked. Such a simple fix to prevent such a dangerous attack - it's obvious and that's why it gets missed.

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.

March 27, 2014 16:56
Best policy is to *never* store user uploaded content into an IIS content folder. Store it somewhere else where no direct URL can get at it, whenever possible. (it does mean you'll need to build a handler to serve it, but that's a small price to pay for security). That's just my humble opinion...
March 27, 2014 17:47
@Tomas: that is true when you are coding the CMS , but what if you are using others CMS , then adding policy to that folder will be the best choice .
March 27, 2014 17:58
I've seen this quite a bit with poorly setup Wordpress sites. I got an email from Amazon AWS saying that one customers Wordpress blog was serving up a phishing site. I took it offline and went though all the rubbish that had been uploaded and it was certainly an eye-opener. There was a single php file which gave a whole admin console with numerous 'tools' which you could run on the server. It even had an option to try all the exploits and return a list of the successful ones!
March 27, 2014 18:06
how do you restrict when you are uploading files to sharepoint from .net ?
March 27, 2014 18:13
Its always better to block the user from uploading everything and white listing whats allowed instead of trying to blacklist certain extensions.
March 27, 2014 18:14
Also, a lot of time people will hide malicious code in a legitimate file, but hide it by creating lots of horizontal space through spaces. This way any casual glance won't show any odd code in the file.
March 27, 2014 18:21
Maybe the developer was trying to re-invent WebDAV?

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.
March 27, 2014 20:35
An application serving documents.

The access to confidential documents were filter by this querystring parameter:


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

March 27, 2014 21:56
@Sam: True, but you'd be surprised how many enterprise applications accept user uploads, store them on temp folders for processing, and turns out the temp folders are URL accessible :/

So it's definitely not only for CMS you should be careful!
March 28, 2014 0:58
Here's a good gotcha... We had code in place to ensure only file names ending in 'jpeg' or 'jpg' could be uploaded. Unfortunately IIS will process a request for a file with an extension of '.asp;jpg' as a classic ASP request, and execute the file as a script. Not checking where the dot was turned out to be a very time consuming mistake.

We adopted the web.config trick as a second line of defence. It seems to work well even for classic ASP sites.
March 29, 2014 23:04
The update for the web.config is not enough because the uploader can upload a web.config in the upload folder and overwrite the handler configuration so I think the web.config should be updated this way:

<location path="upload" allowOverride="false">
<handlers accessPolicy="Read" />

This will prevent the sub folders to override the handler.
March 30, 2014 14:33
I realize it's a CMS so this doesn't completely apply, but don't store the same file name on disk.

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.
March 31, 2014 19:29
Great back to basics tip. A great reminder for general developers to stay security conscious. There are varying degrees of infosec need and ability but every developer can benefit from a reminder to think critically and to question all aspects of an application.
April 01, 2014 9:52
If you must store the file in a web accessible folder, generate random file names without extension (and serve them through a handler. store meta data in db).
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.
April 01, 2014 21:59

dim input = txtusername.text

cmdSQL = "SELECT * FROM user_table WHERE username=" + txtusername.text
April 07, 2014 20:31
I don't usually see a need to store user data in a web-accessible folder, but if you do (eg for forum avatar images) just create a folder called Uploads inside the App_Data folder. It will inherit the security restrictions IIS already provides for the App_Data folder. You can still read files from this folder in C# land but you can only serve them through IIS by creating a simple HTTP handler. You should *also* limit the uploader to only save files of the extension you are expecting (eg .jpg/.jpeg). Any HTTP handler you create to serve files from non-web folders should be hard coded to only ever serve from a whitelisted set of extension (ie only opt-IN image extensions rather than trying to opt out (or block) .aspx etc).

Often this approach is easier than trying to work with an external folder (esp in some 3rd party hosting environment).
April 09, 2014 12:59
If we set the accessPolicy to "Read" in the upload folder then how come the uploader still works? Doesn't it need the Write permission too to, well, write in the folder?
April 16, 2014 0:33
Never allow uploads to the same domain, even outside of the web root! Different TLD means no access to site cookies (so no session hijacking), no execution rights, no handlers - if set up properly (also improves hosting performance). User-provided files must be presumed malicious. No file type is safe. Start exploring the subject by searching the internet for "GIFAR".
May 21, 2014 18:15
Actually it looks like the upload folder is ASP.NET application subfolder which is bad in first play for upload files. Now she has to remember to update this setting each time location changes. Not to mention it's blacklisting. We should always do whitelisting...

Comments are closed.

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.