A couple of months ago, Toby Beaumont reported an ASP.NET vulnerability that, depending on the server configuration, allowed anyone to completely bypass user authentication and access protected files. Microsoft quickly provided a fix and the issue passed without much fanfare, mostly because the flaw wasn't widely exploited, and consequently many people failed to recognize just how serious this attack vector could be.
For nearly a decade, as the freedom of the Internet gave way to anarchy, IIS was the target of countless file access and canonicalization exploits. But Microsoft responded with an aggressive overhaul that resulted in IIS 6, a Web server that is surprisingly secure, even with a default installation. In fact, they did such a great job that IIS security has since become a boring topic.
Although ASP.NET has had some problems, it too has held up fairly well. But this last flaw revealed that ASP.NET has the potential for serious vulnerabilities. It's not the vulnerability itself that concerned me, but what this vulnerability told us about the foundations of ASP.NET file access.
In a way, it reminds me of the USA PATRIOT Act, passed in response to the terrorist attacks of September 11th, 2001. Despite concerns over privacy and the potential for abuse, the new law has not personally affected anyone I know. Nevertheless, it still troubles me, because it messes with fundamentals without anyone completely understanding the future impact of these changes. We really cannot anticipate what problems we might encounter, especially when this law is combined with other future laws. If you never mess with basic civil liberties in the first place, you never have to worry about these complexities in the future.
That is why this ASP.NET issue concerns me. This isn't politics, but I see basic rules broken that might lead to complex future issues.
The specific flaw Beaumont found was deceptively simple: by using a backslash instead of a forward slash you could access secure ASP.NET resources that normally required authentication.
So, if accessing www.example.net/secure/private.aspx is supposed to require authentication, anyone who wants to could still access the file by entering the URL as www.example.net/secure\private.aspx (or using %5C instead of the backslash in IE). Even if you set NTFS permissions to block anonymous users from accessing the file, ASP.NET still allowed access.
As simple as it was to exploit, the existence of the bug told us a lot about ASP.NET's basic security posture -- none of it good:
- ASP.NET was not always using NTFS permissions to enforce file access.
- You can fool ASP.NET by disguising the file path.
- ASP.NET did not properly filter URL requests.
- ASP.NET authentication fails open rather than failing closed.
It turns out that the problem was not with ASP.NET's authentication code, it was an authorization issue. Authentication validates a user's identity, but authorization is what determines if authentication needs to take place. On a typical website, some resources are available to everyone while other resources are only available to authenticated users.
The ASP.NET authorization code determines if the resource requires authentication or not by checking the configuration file of the current application, and looking for rules that match the requested URL. If the URL does not match any of those rules, it checks the configuration of the parent application for a match. If it still finds no match, it continues up to each parent application until it reaches the machine configuration. By default, the machine configuration allows anyone to access anything without authentication.
This means that if you can disguise a URL so that it doesn't match any rule, you will eventually end up at the default rule that says there is no need to authenticate you to access this file.
In other words, if ASP.NET thinks everyone is authorized to access the file, it won't bother running its authentication code to see if a particular user is authorized to have access. ASP.NET opens the file with the security context of the ASP.NET machine account (ASPNET), unless you specifically configure the application to use impersonation. Therefore it completely bypasses any NTFS permissions you might have set on the file.
While there were certain limitations that prevented widespread exploitation of this particular vulnerability, the fact that it was even possible should have been an alarming announcement. The fact that they did not follow such basic best practices brings into question what other vulnerabilities might exist.
Sure, there might never be another serious ASP.NET vulnerability; and if there are any, they might never be publicly known. But that really doesn't matter, because that's not the point. The point is that you must code defensively and follow best practices from the beginning even if there are no foreseeable weaknesses with your code.
I give the IIS and ASP.NET team much credit for what they have accomplished so far, but we are all facing a new standard. I'd like to see them compile a list of specific best practices that they will never, ever break. Stuff like saying they will always filter URLs, or they will always fail closed. And then I'd like to see them publish this list to demonstrate their willingness to stick to these rules.
This obviously isn't just a Microsoft problem, we could all certainly learn from this lesson. But that doesn't mean Microsoft can't take the lead in tackling this problem. Whether you are talking about politics or programming, the concept is the same: follow best practices.
Mark Burnett is an independent security consultant and author who specializes in securing Windows-based servers. He is co-author of the best-selling book Stealing the Network (Syngress), and has also co-authored or contributed to several other books, including Special OPS: Host and Network Security for Microsoft, UNIX, and Oracle (Syngress); Maximum Windows Security (SAMS); and Dr. Tom Shinder's ISA Server and Beyond (Syngress).