I love ELMAH – this is one those libraries which is both beautiful in its simplicity yet powerful in what it allows you to do. Combine the power of ELMAH with the convenience of NuGet and you can be up and running with absolutely invaluable error logging and handling in literally a couple of minutes.
Yet, as the old adage goes, with great power comes great responsibility and if you’re not responsible with how you implement ELMAH, you’re also only a couple of minutes away from making session hijacking of your ASP.NET app – and many other exploits – very, very easy. What’s more, vulnerable apps are only a simple Google search away. Let me demonstrate.
Update: I want to make it clear right up front that the out of the box ELMAH configuration does not make any of what you’re about to read possible. It’s only when ELMAH is configured to expose logs remotely and not properly secured that things go wrong.
The ELMAH value proposition
Some background first; ELMAH is the Error Logging Modules and Handlers written by the very clever Atif Aziz and it’s very popular. How popular? It’s presently the 14th most downloaded package on NuGet:

What this means is that at the time of writing, there have been 42,429 downloads of the library.
I suspect the popularity has a lot to do with how simple it is to implement. First of all, you can add ELMAH to your ASP.NET app without a recompile; it’s simply a matter of some web.config entries and including the ELMAH library.
Secondly, it’s very easy to log errors to a number of different persistent storage mechanisms including the default in-memory store and to SQL Server (among others) for a bit more longevity. Once the web.config is set up, it just happens automagically.
Thirdly, it’s very simple to configure ELMAH to fire you off an email when something goes wrong. There’s nothing like being able to actually contact a user with a “Hey, I see you were having a problem” message five minutes after they’ve had issues. People love that sort of proactivity!
Fourthly, it’s very easy to retrieve log entries, you just head off to the /elmah.axd path of the app which invokes a nice little handler to pull recent messages out of whatever repository you’re using.
And finally, ELMAH logs have really, really useful stuff in them for helping you actually fix the problem. But as it turns out, they also have really, really useful stuff in them for helping the bad guys break the application which brings us neatly to the purpose of today’s post.
Hacker-friendly info in ELMAH
Let’s start delving into the detail of what ELMAH gives us, or in this case today, what it gives the hacker. Here’s an example of what comes out of that elmah.axd handler:

Actually, you can see this for yourself over at http://isnot.asafaweb.com/elmah.axd
This particular site is one I use as a test bed for ASafaWeb and its deliberately insecure (more on that shortly). What you’re seeing in the image above is a request for the path “/blah.aspx” which doesn’t exist hence it’s causing an HTTP 404 “NOT FOUND” which is captured by ELMAH. So far, so good.
Drilling down into that error, we’ll see a stack trace which begins to disclose the internal implementation of the code where the error occurred. Keep in mind this is totally independent of the custom errors configuration of the app; you can turn them on and provide a default redirect to an error page and ELMAH will still log what you see below – that’s the beauty of it!

Now let’s scroll down a bit and get to the interesting section – server variables:

I’ve highlighted two sections here and I want to refer to them from the bottom up:
- The “AUTH_USER” variable is set to “admin”. This is the username of the authenticated user when the error occurred. In other words, we know it was the admin logged in at the time.
- The “.ASPXAUTH” cookie. In case this isn’t already familiar, take a look through my post about OWASP Top 10 for .NET developers part 9: Insufficient Transport Layer Protection
Read the post? Right, so now you’ll have a good idea of where this post is heading – the .ASPXAUTH cookie is used to persist the authenticated state of a user when the website uses the ASP.NET membership provider for authentication.
Identifying a target
The crux of this exploit centres around the fact that many people are not properly protecting ELMAH logs on their site and that it’s easily discoverable. In fact it’s so easy to discover, it’s just a matter of a simple Google search for inurl:elmahtr.axd ASPXAUTH

Oh boy, that’s a lot of results – 11,000 unprotected ELMAH log pages with authentication cookie info! Substitute the “ASPXAUTH” criteria for “Error Log for” which appears at the top of every elmah.axd resource and the result is presently 192,000. Ouch! Sure, some of these results are for the same site and some are simply pages about ELMAH but whichever way you cut it, there are a huge number of sites putting their privates on public display!
This is your classic Googledork or in other words, a carefully crafted search which turns up results related to careless configuration. Keep in mind also that this is obviously only the publicly accessible results which Google has indexed; how many more sites are out there exposing their ELMAH logs that simply haven’t been indexed? After all, elmah.axd is normally not a publicised resource; Google has to know it’s there and explicitly request the resource for indexing.
Exploiting the website
Now for the interesting bit – leveraging the info above to actually exploit the website. Let me paint a scenario which puts the ease and practicality of this into context:
Wearing my evil hacker hat, I’ve just done the Google search above and identified the site I want to exploit. I can see from the logs that the ASPAUTH cookie is being captured so I know that forms authentication is being used or in other words, the site has something that it wants to protect. From this search alone there’s a good change I could find a valid ASPXAUTH token to use in my hijacking exercise.
But what if the log is just using the default in-memory storage and has recently been flushed? Or there are no recent errors with an ASPAUTH cookie which is still valid? No problem, just a little bit of social engineering is required to help generate a new error message. Finding contact details for the site owner is usually simple, let’s try a message like this (I’m assuming @asafaweb is the target):

The inclusion of the “<” character in the URL will cause request validation to fire – everything else in the message is just intended to build a sense of urgency (the message) and legitimacy (the domain of the URL). Now of course the user needs to be authenticated in order to get a new ASPAUTH cookie, but chances are they’re either already logged in or are using a long-lasting timeout to save them logging back in. Even if they’re not, a similarly crafted message with a link to an admin page could easily take care of this.
Now it’s just a matter of the attacker watching ELMAH until a new log entry appears. The auth cookie will look something like this:
.ASPXAUTH=3C886BA2344099338361C921C846EAF4E02F2A88E5E7EDE6838705928F7BB7C6FF469D35FEB1532C44B81DB38F200DEE08B6ED0E6121B945C659E932D8CE8B69FFF09E7B59DBE4820873DBD7891DD6B6BC4A486F35A2F99849017A6C72D9C6A44517D9AFDC731B3A3C55596E79732806F7DDDF9F
With our hacker hat on, let’s now take this value and create a new cookie with the name and value from above. This becomes very simple with a browser extension like Edit This Cookie:

You can see the “Log In” text behind the cookie window so this browser is definitely not authenticated before adding the cookie. But if the hacker submits the cookie and refreshes the page:

And there you have it – the hacker is now logged in as an admin! This doesn’t give them the admin password, but it does them all the rights of the admin user. Depending on the system, this may give them the rights to view or create other accounts, manage permissions, view financial data, etc. etc. Use your imagination.
But wait – there’s more
A publicly exposed ELMAH log is pretty serious business vulnerability wise. There’s not just the risk of session hijacking as explained here, for example there’s the risk of disclosing internal database structure just by searching for SqlException:

Or how about a bunch of SQL statements – this is just what you want to get a big head start on an injection attack:

Or how about simply searching for “password” – you don’t even need to look beyond the search window on some of these:

Want to find sites using a particular library in which a zero-day has just been discovered? And this is absolutely, positively only an example – I’m just picking a popular library which wouldn’t normally be discoverable by just browsing the site:

But of course it’s not just about searching for existing errors which might be of interest, once an attacker knows a site is exposing ELMAH logs they can then go and attempt a whole range of other attacks and get immediate feedback about what’s going on internally! How convenient is that?!
But there’s also a whole raft of other little snippets which might come in valuable to the evil doers; referring URLs, physical path of the site on the server, physical path of the site on the machine it was compiled on (often the developer’s machine), the names and values of all the cookies, the IP address of visitors to the site and so on and so forth. ELMAH logs are a treasure trove of information.
Protecting against this attack
This is really just a simple case of access controls or in OWASP speak, Failure to Restrict URL Access. This is not – and let me really emphasise this – is not a vulnerability in ELMAH.
In a case where the membership and role providers are being used, the fix is nothing more complex than a simple authorisation entry in the web.config:
<location path="elmah.axd"> <system.web> <httpHandlers> <add verb="POST,GET,HEAD" path="elmah.axd"
type="Elmah.ErrorLogPageFactory, Elmah" /> </httpHandlers> <authorization> <allow roles="Admin" />
<deny users="*" />
</authorization> </system.web>
<system.webServer> <handlers> <add name="Elmah" path="elmah.axd" verb="POST,GET,HEAD"
type="Elmah.ErrorLogPageFactory, Elmah"
preCondition="integratedMode" /> </handlers> </system.webServer> </location>
It’s important to note that the ELMAH handler has been moved into the “admin” path; failure to do this may allow elmah.axd to be mapped to other paths such as “/foo/elmah.axd”. This would mean the logs could be accessed even if the path “elmah.axd” were to be secured as the authorisation rule would only apply to the handler in the root of the site.
Update 11 Jan: The guidance above has been slightly modified based on the discussion in the comments below. It seems that the original guidance on the ELMAH website which I reproduced here allowed the same elmah.axd handler to be requested from other paths hence bypassing the authorisation. Bugger!
Update 18 Jan: Atif has left a comprehensive comment below and has now changed the security guidance on ELMAH to add the handler under the location with a path of elmah.axd hence allowing it to be securely called from the root.
That’s it, nothing more. Those 192,000 sites from the Googledork search do not have this! Each of those sites is easily vulnerable to the attack above. They’re also exposing internal stack traces and server variables which may lead to attacks of other natures. This is a serious, serious configuration vulnerability.
Oh, and just in case it’s not already obvious, transport layer protection does absolutely nothing to mitigate this risk, it just means an attacker can load your sensitive log data over an encrypted connection :)
A helping hand from ASafaWeb
In fairness to those with publicly facing ELMAH logs, this is really easy to get wrong. The ease of implementation ELMAH offers makes it both powerful and potentially vulnerable at the same time. In fact that’s often the story with .NET in general; features like custom errors and stack traces can very easily be exposed entirely by accident.
Last month I launched ASafaWeb with the intention of providing a free tool to easily check for ASP.NET configuration related vulnerabilities. Today I’m happy to also add a scan for the public accessibility of ELMAH.
I’m very careful with any scans I add to ASafaWeb. Usually this means an additional HTTP request (as is the case with the ELMAH scan), and these are very costly little exercises in terms of the duration it adds to a scan. But in the case of ELMAH, the prevalence of the library combined with the enormous number of insecure sites and the ease of implementation makes it a good candidate to add.
Here’s how it works; the ASafaWeb website allows you to either plug in a URL (this can be any publicly accessible URL) or alternatively, run a scan against the sample site I referred to above and have highlighted below:

When the scan runs, there are a series of HTTP requests made in order to test various aspects of the site security. ASafaWeb shows you what the specific requests were then the status of each individual scan. Sometimes more than one HTTP request is used for a scan or a single request can be reused across multiple scans:

Clicking on the failing “ELMAH log” scan then jumps us down the page to the details of the scan including the path with the vulnerability and how to fix it (essentially the information in the post above):

You can deep link directly into the scan of the test site if you’d like to see it in action now.
Summary
In case I didn’t make it perfectly clear the first few times, this is not a flaw in ELMAH, in fact I think it’s a fantastic tool and I use it extensively in ASafaWeb: https://asafaweb.com/elmah.axd
Whoops, you can’t access that though, can you?! And that’s really the point I’m making – ELMAH can be implemented securely and everything above is no way a recommendation not to use it. But please, please, apply a little due diligence and lock it down properly.
If you do discover your ELMAH logs were publicly visible then decide to lock them down, there’s still the real risk they’ve already been indexed and cached versions are still available, in fact I saw this several times when researching for this post. If you’re in this camp, you want to take a good look at what’s in your (now secure) ELMAH logs and consider what information may have been exposed and is now searchable via the various search engines (remember, it’s not just Google).






Software architect and Microsoft MVP, you’ll usually find me writing about security concepts and process improvement in software delivery.





27 comments:
The NuGet package should be updated to disable access to the .axd through the configuration.
By default, remote access is disabled but of course most people (understandably) want to be able to access it remotely so they enable it: http://code.google.com/p/elmah/wiki/SecuringErrorLogPages
Of course that links also explains how to secure it but as you can see from the post above, this guidance often isn't followed.
I think it's important to mention this up front in the build up to your story. Most people may panic and not get as far as the comments section to read that remote access is disabled by default, even when it's installed in a snap using the NuGet packages. They might just hit uninstall a third of the way into your blog entry. ;) I had a hunch that people will be throwing ELMAH into the wild, as in "Look boss, I even took care of error logging!" and which is why remote access is disabled by default.
That's a very fair point Atif and it should be clearer. I've just added an update to the intro to make it crystal clear.
I'd also like to link to a series of blogposts I've written about securing ELMAH with Basic/Windows authentication and still be able to use Forms authentication for the main website:
http://tech.kipusoep.nl/2012/01/06/umbraco-elmah-with-sql-ce-4-0-and-authentication-part-2/
When you say, “…with the default ELMAH configuration”, you really mean the default configuration tweaked irresponsibly with the one setting that allows remote access. :)
I've got to be more careful around you Atif! Of course I was referring to the default handler path but yes, I was being a bit (deliberately) irresponsible in the configuration.
But you can also access
http://isnot.asafaweb.com/test/hacker/elmah.axd
Your web.config entry will only handle http://isnot.asafaweb.com/elmah.axd
not
http://isnot.asafaweb.com/test/hacker/elmah.axd or
http://isnot.asafaweb.com/test/elmah.axd etc
I realise that you don't need the /admin directory, but it simply makes it easier to secure properly.
Cheers,
James
Hi Troy,
My point is that your advice doesn't secure ELMAH - particularly if you are dealing with a WebForms site!
(Though routing in MVC will invariably do much of the work for you)
http://isnot.asafaweb.com/test/hacker/elmah.axd
http://isnot.asafaweb.com/test/elmah.axd
http://isnot.asafaweb.com/elmah.axd
(and any combination of paths) are all accessible by default.
Securing elmah.axd only secures
http://isnot.asafaweb.com/elmah.axd
it does not secure the rest!!
Hope this helps!
Cheers,
James
Thanks Troy. Great post and keep them coming. I was just looking for a module similar to this. You can tell Atif, he has one more adopter of his great stuff.
This is a great piece of writing - once again you've taken what some people may assume everyone knows and brought it into the light.
I always add some security through obscurity ontop of securing elmah by renaming the handler to something more generic (not /elmah.axd).
it's probably worth adding ontop of atif's comment that the default installation for elmah is to have remote access off. The default web.config that comes with it has this set to false as well.
I'm afraid JamesDriscoll is correct I applied your web.config fix to my MVC 3.0 site and now /elmah.axd is not accessible but put something before it eg. /moop/elmah.axd it still displays the elmah log.
That's very odd, certainly it's not consistent with the behaviour I'm seeing on ASafaWeb which is also MVC 3. Take a look: https://asafaweb.com/moop/elmah.axd
Regardless, I've passed the feedback onto Atif, it will be interesting to see his comments.
Troy,
My guess is that your MVC routes have only been opened up for http://asafaweb.com/elmah.axd
Nazmul probably has a route open for */elmah.axd
The important point you are missing is that when you set the handler up as "elmah.axd", it is intercepting calls in all folders for elmah.axd - not just the root folder.
When you restrict with location="elmah.axd", you are only applying that security in that specific folder.
This is why I say that your guidance needs updating - as does the Securing Error Logs wiki page!! Something I've added to my TODO list.
Please carefully re-read Haacked's post (linked above) - particularly paying attention to the bit where he explains why he changes the handler to "/admin/elmah.axd".
Hope this helps!
Cheers,
James
its always a good to know something with live example ,
and your "words Use your imagination" inspires ..!
then i start googling...after 15 mins i back to your site and start similar method on your site.
and i found http://tinypic.com/r/25fnfqs/5
Hi Nimiaj, yes, that's the intentionally insecure ELMAH log at:
http://isnot.asafaweb.com/elmah.axd
It's the one I referenced in the post above. Nice to know that Google has now indexed it!
Just fantastic,
I am big fan of you now.
thanks
Ah, the penny has just dropped in terms of your contribution to ELMAH!
The penny also just dropped on what you're saying and why I'm not observing that behaviour: Your point is that the handler will match the "elmah.axd" pattern in any path whilst the authorisation rule only secures it in the root. The reason I'm not seeing that in ASafaWeb is because by default, a new MVC 3 project registers the following "ignore" route (wonder if this is in Nazmul's global.asax?):
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
I'm assuming the implementation of the ELMAH handler then expressly adds the root path but all others are caught by the above statement. Without that axd wildcard path being ignored in your web forms app, the handler is being mapped to the likes of http://isnot.asafaweb.com/foo/elmah.axd and http://isnot.asafaweb.com/bar/elmah.axd which of course wouldn't be protected by the authorisation guidance above or on the ELMAH site.
I've just updated the guidance above and on the ASafaWeb scan to be consistent with placing ELMAH beneath an admin path. Actually I've followed the pattern discussed on the Stack Overflow question which is worth a read for some more background.
James, thanks for doggedly persisting with me on this one, I should have looked into it further earlier on. It would be great to get that ELMAH wiki updated given it's an authoritative source. In the meantime, it gives me a nice little spin to add to the end of the ASafaWeb scan :)
Just to warn you, https://asafaweb.com/elmah.axd is publicly available.
Great post and you should really improve it with JamesDriscoll suggestions.
Thanks, you caught me right in the middle of re-publishing after the update just now about putting it in an "Admin" path. The demons are now vanquished :)
Although I adamantly believe in being cautious and not making assumptions when it comes to security, what are the ramifications when using ELMAH in an environment that uses Windows Integrated Security?
ELMAH works just fine in a Windows environment using integrated authentication. If the app is using the Windows authentication scheme (the default setting) and IIS is enabled to allow it, you can apply the same process as used in forms auth to protect the resource by username or role.
Nice post. I disagree with your assessment that this is not a vulnerability in Elmah. Even if I restrict access to elmah logs to single person, that person should not be able to impersonate users. While better than opening your fly to the whole world, it is still not acceptable by any serious standards. The real solution would be to have Elmah NOT store the values of auth/roles cookies BY DEFAULT and make that list configurable.
I wouldn't call that a "vulnerability" in ELMAH per se, but I see where you're coming from. Of course if ELMAH is properly secured then the only people who have access to it will quite likely also have access to the underlying database and have the ability to simply recreate password hashes anyway. Perhaps configurability of logged parameters is the key, it might be something you want to put in the issue log if you use ELMAH:
http://code.google.com/p/elmah/issues/list
Really valuable information, Great work.
It would be nice if the ELMAH check would check the URL entered plus the root. We are finding developers have added ELMAH in subdirectories.
Thanks for the feedback, I assume you mean "it would be nice if ASafaWeb could check the URL entered plus the root". The problem is that the app root (and consequently where ELMAH is normally found) could be any level of directories deep. If someone enters a URL with a path such as "/a/b/c/", I have no idea where the root is and where to check for ELMAH. I could check the path entered but most of the time this is not the root and it means an additional - and expensive - HTTP request.
I am conscious of this, and it's the same problem when checking for trace.axd. Fortunately the vast, vast majority of sites run from the root of the domain so whilst the results aren't always going to be perfect, it's a very high hit-rate and a reasonable trade-off to keep the performance up.
Post a Comment