Why can I upload a file without IIS Write Permission?

A frequently misunderstood aspect of IIS is that disabling “Write” permissions from IIS Management UI actually prevents anyone from writing files to the server through IIS (such as upload files). This is clearly not the case, as the following question suggests… so is this a security hole in IIS6, or is there another explanation?


A customer have a IIS 6 web server and even with IIS Write property DISABLED, an ASPX form can upload files to the server.

The authentication is Anon (via IUSR_ user) and the IUSR_User have RWXD rights on the folder where the upload is stored.

In the properties of the IIS folder where upload is done, the Read permission is set, but Write, SourceAccerss and Browse are disabled.

Why the upload works???


Yeah, we see lots of incredulous/frantic posts like this one in microsoft.public.inetserver.iis.security because the user thinks that it must be a security bug in IIS. After all, I just disabled “Write” from IIS, so why can a remote user still write to my server?

The short answer to this question is that everything the user observed is correct and by-design. The user just failed to configure what he thinks he configured, and IIS can do nothing to save you from your own misunderstanding…

The long answer

ASP.Net, IIS, and the NTFS FileSystem are all separate systems running on top of Windows. Since they can function independently from each other, they each have their own authentication, authorization, and access-control mechanisms, and they are all configured through different mechanisms in different configuration stores.

These mechanisms all have to be configured and interacting correctly in order for a logical concept, like “uploading a file”, “browsing a directory”, “running an ASPX page”, etc to function. When they are not configured correctly, you see people asking questions about “why am I denied access”, “why am I getting Forbidden”, etc… but that’s for another discussion another day.

Now, you may wonder why have all these different authentication/authorization/access-control mechanisms at all. After all, I just want to prevent users from writing files to my server or prevent them from reading a file – so just give me a simple “Allow/Deny” checkbox and do what I want! Isn’t that what the IIS Manager UI means with the “Read” and “Write” check boxes?

Unfortunately, that is not the case. IIS, ASP.Net, and NTFS are all too configurable to give you this simplified view of life. The IIS Manager UI only configures settings at the IIS scope, so the the “Read”, “Write”, “Script source access”, and “Directory browsing” check boxes only affect IIS-level behavior. What they translate into is:

  • “Read” access controls whether the HTTP GET verb is allowed. In other words, can you retrieve an HTML, GIF, or CSS static file from the web server. The astute reader should note that without Read access, you can still send the GET verb to scripts like an ASPX Page, and they will STILL execute and generate responses.

    Is this a bug? Not really… consider the following: for an URL whose resource extension matches an application mapping, does IIS actually read and execute the script file named by the URL, or does it execute a script handler binary which parses the URL to determine what script content to actually execute and then generate a response?

    Hint: you allowed “Scripts” execution permission, and GET is allowed by the Application Mapping, so IIS can execute the script handler. What the handler does with the URL is up to it; it can use the URL to look up content from a database and execute that content and have NOTHING to do with the filesystem.
  • “Write” access controls whether HTTP PUT verb is allowed. This verb allows the client to send entity-body and “put” it at the physical location corresponding to the URL being “put”.

    i.e. If the URL “/” corresponds to the physical location C:\inetpub\wwwroot, then a PUT /file.txt request will create C:\inetpub\wwwroot\file.txt and its contents will be the entity body sent on the request.
  • “Script source access” controls whether to allow the DAV “Translate: f” command, which allows the client to retrieve the unprocessed source code of a script instead of the executed response generated by the script source code.

    i.e. If you have an Application Mapping of .asp to ASP.DLL and “Script source access” is enabled, then a GET /foobar.asp request with the “Translate:f” header will return the unprocessed source code of foobar.asp. Otherwise, GET /foobar.asp will return the response generated by executing code contained within foobar.asp.
  • “Directory browsing” controls whether IIS will send back a listing of a directory if you make a request to the “/” of a given virtual directory and no default document exists. Thus, if you always have a default document defined and existing, directory browsing will never happen.

As you can see, these access controls in IIS have nothing to do with allowing/denying uploads, so enabling “Read” while disabling “Write”, “Script source access”, and “Directory browsing” definitely do nothing as far as denying ASPX upload is concerned.

What about the Question?

What do I think is actually happening in your situation?

  • Since anonymous authentication is enabled, IIS will use the user token of the configured anonymous user identity in IIS for all remote, unauthenticated requests.
  • You configured ASP.Net to either impersonate (<identity impersonate=”true” /> in an ASP.Net .config file), or you configured the ASP.Net worker process identity to be the configured anonymous user identity in IIS (<processModel userName=”IUSR_UserName” password=”IUSR_Password” /> in an ASP.Net .config file). This controls the user identity ASP.Net uses to execute the ASPX page that accepts the upload.

    I suspect you have the former configuration because the latter takes much more effort to misconfigure.

  • You ACLed the directory to give write access to the configured anonymous user identity in IIS, so the ASPX page could write there.

Now, since ASPX is arbitrary code, it can do whatever it wants when it runs, including writing something to some directory. IIS configuration can only control whether this code is executed at all (i.e. does IIS allow a handler to execute the URL that represents this ASPX page).

So, how would you disable uploads in your situation? Try changing any of the above three parameters that I mentioned so that remote requests do not run with a user identity that can write to the directory in question, and uploads should stop happening.


Comments (8)

  1. Ken Cox [MVP] says:

    This is a helpful analysis.

    What users need is a security tool/debugger that analyzes a given Web’s configuration and reports on who has access to do what. It needs to take into account the convergence of ASP.Net (including impersonation), IIS, and NTFS security settings.

    Web site owners should be able to see at a glance whether the security is actually what they intended.


    Microsoft MVP [ASP.NET]

  2. wpoust says:

    Excellent post.

    When I use online help in IIS 5.0 to look up the write privilege I got, "Use this property sheet to change properties for a physical directory in your Web site."

    Can you see why people would be confused about the IIS write permission? Do you see why people would stop using online help all together?

    Thanks again for the post.

  3. David Wang says:

    Ken – That is a good idea and is something whose bits and pieces that I have been privately writing and tossing together for people to use.

    We also tried to do this with tools like AuthDiag which just tackle this problem from an IIS perspective without ASP.Net, and the results are already mixed. I believe the most useful part of the tool is not its auto-analysis reports for the layman but rather its incisive probes which gives insight to professionals that can make sense of it all.

    I just think the problem space is very complex, and a self-serve silver bullet nearly impossible.

    Thus, I believe in tools that make the underlying information easier to discover, aggregate, and consume. Ultimate responsibility of interpretation should lie in the user’s hands.

    wpoust – Thanks. I try to elucidate as much as I can. I am not going to pretend that I like IIS product documentation regardless of distribution format. I just know that prior to IIS 6, IIS documentation was not in good shape. We fixed a lot of things in documentation for IIS 6, but there is still a lot more to fix. I also know that documentation quality varies from team to team across Microsoft, so I caution against transplanting prejudice of one product to another, or even one version to another.


  4. George Dennie says:

    Actually, this is a big security issue and should be regarded as a security hole. Complexity is itself a barrier (foundation of cryptography). Essential this complexity implicitly prevents people from securing their systems reliably.

  5. David.Wang says:

    George – I agree with the statement that complexity makes everything, including security, harder.

    However, I think the problem is that end-users want cheap software customized to their unique situations and they want it to be simple to administer. And until we build successful AI, I simply do not believe it will ever reconcile.

    By its nature, cheap, customizable software which amply satisfy the unique environment of any user will consist of interacting "layers" that compose to form new functionality. But the same composable layers that bring cheap/customizable solutions also expose interactions that increase complexity.


  6. Excellent article. Just wondering… could you write one along the same lines adding user permissions on SQL server objects to the mix?

  7. bruno cabral says:

    Put the user

    "NT AUTHORITYAuthenticated Users"

    under Administrators Groups and you are Free to write/read/execute !!