Click here to Skip to main content
Click here to Skip to main content

Security: Protect against POET Attacks with Custom Errors!

, 21 Sep 2010
Rate this:
Please Sign up or sign in to vote.
Protect against POET Attacks with Custom Errors!

There’s been a big deal made of a serious security flaw in ASP.NET which potentially affects a lot of .NET sites, that allows a 3rd Party to trick ASP.NET into serving sensitive files within a web application folder.  Microsoft has released an official advise on how to temporarily patch the problem which revolves around forcing Error and Page Not found pages to return the same status page. This would need to stay in place until a permanent fix is released.

This workaround clearly introduces an usability issue, which the client may not accept.

Fortunately a quick amend to my Custom Error Module can secure your site against the attack with minimal impact to usability.

 using System;
 using System.Web;
 using System.Net;
 using System.Collections.Generic;
 using System.Configuration;
 using System.Web.Configuration;
  
 namespace MartinOnDotNet.Website.Support
 {
     /// <span class="code-SummaryComment"><summary>
</span>     /// Handles errors in an SEO friendly manner
     /// <span class="code-SummaryComment"></summary>
</span>     public class SeoErrorLoggingModule : IHttpModule
     {  
         private static System.Random random = new Random((int)DateTime.Now.Ticks);
  
         private const int MaxDelay = 500;
  
         private static CustomErrorsSection customErrors = 
		WebConfigurationManager.GetSection("system.web/customErrors") 
			as CustomErrorsSection;
  
         /// <span class="code-SummaryComment"><summary>
</span>         /// Called when [error].
         /// <span class="code-SummaryComment"></summary>
</span>         /// <span class="code-SummaryComment"><param name="sender">The sender.</param>
</span>         /// <span class="code-SummaryComment"><param name="e">The <see cref="System.EventArgs"/> 
</span>         /// instance containing the event data.<span class="code-SummaryComment"></param>
</span>         protected virtual void OnError(object sender, EventArgs e)
         {
             HttpApplication application = (HttpApplication)sender;
             HttpContext context = application.Context;
             if (context != null && context.AllErrors != null)
             {
                 foreach (Exception ex in context.AllErrors)
                 {
                     ex.Data["RawUrl"] = context.Request.RawUrl;
                     HttpException hex = ex as HttpException;
                     if (hex != null && hex.GetHttpCode() == (int)HttpStatusCode.NotFound)
                     {
                         Logging.Logger.LogWarning(string.Format
			(System.Globalization.CultureInfo.InvariantCulture, 
			"Requested File Not Found {0} ({1})", context.Request.RawUrl, 
			context.Request.Url));
                     }
                     else
                     {
                         Logging.Logger.Log(ex);
                     }                    
                 }
             }
             HttpException httpException = context.Error as HttpException;
             context.Response.Clear();
             if (httpException != null && !IsResourceRequest(context.CurrentHandler))
                 context.Response.StatusCode = httpException.GetHttpCode();
             else
                 context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
             if (((context.IsCustomErrorEnabled && !context.Request.Browser.Crawler) 
			|| IsResourceRequest(context.CurrentHandler) )
                 && !IsAnErrorPage(context.Request.RawUrl))
             {
                 System.Threading.Thread.Sleep(TimeSpan.FromMilliseconds
						(_random.Next(MaxDelay)));
                 context.ClearError();
                 string path = GetPathForError(context, 
			(HttpStatusCode)context.Response.StatusCode);
                 if (!string.IsNullOrEmpty(path))
                 {
                    if (CustomErrors.RedirectMode == 
			CustomErrorsRedirectMode.ResponseRedirect && 
				!IsResourceRequest(context.CurrentHandler) )
                    {
                        context.Response.Redirect(path, true);
                    }
                    else
                    {
                        context.RewritePath(path);
                    }
                 }
             }
         }
  
         /// <span class="code-SummaryComment"><summary>
</span>         /// Determines whether current request is to a resource handler
         /// <span class="code-SummaryComment"></summary>
</span>         /// <span class="code-SummaryComment"><param name="handler">The handler.</param>
</span>         /// <span class="code-SummaryComment"><returns>
</span>         ///     <span class="code-SummaryComment"><c>true</c> if [is resource request] [the specified handler]; 
</span>         /// otherwise, <span class="code-SummaryComment"><c>false</c>.
</span>         /// <span class="code-SummaryComment"></returns>
</span>         protected virtual bool IsResourceRequest(IHttpHandler handler)
         {
             return handler != null
                 &&
                 (typeof(System.Web.Handlers.AssemblyResourceLoader).
						IsInstanceOfType(handler)
                 || typeof(System.Web.Handlers.ScriptResourceHandler).
						IsInstanceOfType(handler));
         }
  
         /// <span class="code-SummaryComment"><summary>
</span>         /// Gets the path for error.
         /// <span class="code-SummaryComment"></summary>
</span>         /// <span class="code-SummaryComment"><param name="current">The current.</param>
</span>         /// <span class="code-SummaryComment"><param name="status">The status.</param>
</span>         /// <span class="code-SummaryComment"><returns></returns>
</span>         protected virtual string GetPathForError
		(HttpContext current, HttpStatusCode status)
         {
             foreach (CustomError ce in customErrors.Errors)
             {
                 if (ce.StatusCode == (int)status) return ce.Redirect;
             }
             return customErrors.DefaultRedirect;
         }
  
         /// <span class="code-SummaryComment"><summary>
</span>         /// Determines whether the given path (RawUrl) is an error page itself
         /// <span class="code-SummaryComment"></summary>
</span>         /// <span class="code-SummaryComment"><param name="path">The path.</param>
</span>         /// <span class="code-SummaryComment"><returns>
</span>         ///     <span class="code-SummaryComment"><c>true</c> if [is an error page] 
</span>	/// [the specified path]; otherwise, <span class="code-SummaryComment"><c>false</c>.
</span>         /// <span class="code-SummaryComment"></returns>
</span>         protected virtual bool IsAnErrorPage(string path)
         {
             if (ErrorPages != null)
             {
                 foreach (string s in ErrorPages)
                 {
                     if (path.IndexOf(s, StringComparison.OrdinalIgnoreCase) >

The amendments hinge on the fact that the exploit only really affects the WebResource.axd and ScriptResource.axd so any error relating to these handlers is automatically given an 500 status (Internal Server Error) and treated as a normal error. This is an acceptable compromise for me as all references to these handlers should be programmatically generated and by your site and therefore ‘correct’.

As per Scott Gu’s recommendation, I’ve added a random <500ms delay to the processing of all errors to help muddy the waters and added support for the ResponseRewrite property on the CustomErrors element.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Martin Jarvis
Software Developer (Senior) Freestyle Interactive Ltd
United Kingdom United Kingdom
I'm a lead developer for Freestyle Interactive Ltd where we create many wonderful websites built on Microsofts ASP.Net and Ektron CMS.
 
I've been developing .Net applications (both Windows and Web) since 2002.
Follow on   Twitter

Comments and Discussions

 
General500ms delay Pinmemberburntass29-Nov-10 8:24 
GeneralRe: 500ms delay PinmemberMartin Jarvis29-Nov-10 8:29 
GeneralMy vote of 5 PinmemberXmen W.K.22-Sep-10 15:19 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.140721.1 | Last Updated 22 Sep 2010
Article Copyright 2010 by Martin Jarvis
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid