2 Comments

A simple command to add to your Visual Studio External Commands menu that will do a sanity check of all your views (and plain old ASPX pages for that matter):

It calls the aspnet_compiler tool on the current web project displaying the results n the Output window:

To set this up, go to Tool > External Tools and add an item with the Command set to to cmd.exe and Arguments set to:

/c echo Build Views for $(ProjectFileName) && if not exist "$(ProjectDir)\web.config" (echo ERROR: Not a web project! && exit) else (call "%VS110COMNTOOLS%\vsvars32.bat" && aspnet_compiler.exe -v temp -p $(ProjectDir) && echo SUCCESS)

If you are running Visual Studio 2010 then change  %VS110COMNTOOLS% to %VS100COMNTOOLS%.

12 Comments

As documented in recent posts, I've been tinkering getting the LESS and CoffeeScript compilers running on Windows Script Host. I've now got round to wrapping these up as ASP.NET HTTP Handlers so you can easily use them in ASP.NET-based websites. You simply reference the *.less and *.coffee files and they get served up as CSS and JavaScript directly. For example:

<link href="content/style.less" rel="stylesheet">
<script src="content/site.coffee"></script>

No need to install add-ins into Visual Studio or add build steps to your project. The main downside is that it won't run on non-Windows platforms under Mono (although I'm tempted adapt it to use Mozilla's SpiderMonkey JavaScript Shell).

If you're running Visual Studio 2010 then simply use the LessCoffee NuGet package.

PM> Install-Package LessCoffee

If you're using Visual Studio 2008 you'll need follow these manual steps:

  • Copy LessCoffee.dll to your web application's /bin directory
  • Add the following entries to your web.config file:
    <system.web>
        <httpHandlers>
            <add path="*.coffee" type="DotSmart.CoffeeScriptHandler, LessCoffee" verb="*" validate="false"/>
            <add path="*.less" type="DotSmart.LessCssHandler, LessCoffee" verb="*" validate="false"/>
        </httpHandlers>
    </system.web>

    <!-- IIS 7 -->
    <system.webServer>
        <validation validateIntegratedModeConfiguration="false"/>
        <handlers>
            <add path="*.coffee" type="DotSmart.CoffeeScriptHandler, LessCoffee" verb="*" name="DotSmart.CoffeeScriptHandler"/>
            <add path="*.less" type="DotSmart.LessCssHandler, LessCoffee" verb="*" name="DotSmart.LessCssHandler"/>
        </handlers>
    </system.webServer>

If you're using Windows 2003/IIS 6 then you will need to map the file extensions *.less and *.coffee to aspnet_isapi.dll.

The source is on GitHub, obv: https://github.com/duncansmart/LessCoffee

0 Comments

This exception has been plaguing our automated error reports inbox for a while:

System.TypeInitializationException: The type initializer for 'System.Drawing.ToolboxBitmapAttribute' threw an exception. 
  ---> System.Runtime.InteropServices.ExternalException: GDI+ is not properly initialized (internal GDI+ error).
  at System.Drawing.Bitmap..ctor(Stream stream)
  at System.Drawing.ToolboxBitmapAttribute..cctor()
  --- End of inner exception stack trace ---
  at System.Reflection.CustomAttribute._CreateCaObject(Void* pModule, Void* pCtor, Byte** ppBlob, Byte* pEndBlob, Int32* pcNamedArgs)
  at System.Reflection.CustomAttribute.CreateCaObject(Module module, RuntimeMethodHandle ctor, IntPtr& blob, IntPtr blobEnd, Int32& namedArgs)
  at System.Reflection.CustomAttribute.GetCustomAttributes(Module decoratedModule, Int32 decoratedMetadataToken, Int32 pcaCount, RuntimeType attributeFilterType, Boolean mustBeInheritable, IList derivedAttributes)
  at System.Reflection.CustomAttribute.GetCustomAttributes(RuntimeType type, RuntimeType caType, Boolean inherit)
  at System.RuntimeType.GetCustomAttributes(Type attributeType, Boolean inherit)
  at System.ComponentModel.ReflectTypeDescriptionProvider.ReflectGetAttributes(Type type)
  at System.ComponentModel.ReflectTypeDescriptionProvider.ReflectedTypeData.GetAttributes()
  at System.ComponentModel.TypeDescriptor.TypeDescriptionNode.DefaultTypeDescriptor.System.ComponentModel.ICustomTypeDescriptor.GetAttributes()
  at System.ComponentModel.TypeDescriptor.GetAttributes(Type componentType)
  at System.Web.UI.ViewStateModeByIdAttribute.IsEnabled(Type type)
  at System.Web.UI.Control.SaveViewStateRecursive()
  at System.Web.UI.Control.SaveViewStateRecursive()
  at System.Web.UI.Control.SaveViewStateRecursive()
  at System.Web.UI.Control.SaveViewStateRecursive()
  at System.Web.UI.Control.SaveViewStateRecursive()
  at System.Web.UI.Page.SaveAllState()
  at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)

The page it occurs on is the login page of an ASP.NET WebForms app which happens to be the default.aspx page in the root of the site: a page that gets a lot of hits. It happened sporadically and was impossible to reproduce: likely some odd timing issue. But when it did happen, it brought the whole web app down.

There appears to be an official hotfix for System.Drawing.dll at http://support.microsoft.com/kb/975410. You will need to contact Microsoft Support for this though. Rather than use up one of our Microsoft Partner support hits, a quick, simple solution dawned on me.

Our solution

Looking at the stack trace, it’s clear that the issue occurs when the page’s ViewState is being serialised. Therefore, the solution (OK, more of a workaround) in our case was simply to disable ViewState on the page:

<@Page ... EnableViewState="false" %>

Now this might not work for you because your page, or controls on your page may rely on ViewState. For example, if you have OnChange event handlers on textboxes: these rely on having the “before” stashed in ViewState. But we rarely use ViewState anywhere in the application, so it was especially galling that we’d left it enabled on one of the the busiest pages in the app.

16 Comments

UPDATE: Patch available, forget the workarounds, install it now: http://weblogs.asp.net/scottgu/archive/2010/09/28/asp-net-security-update-now-available.aspx

If you develop or run an ASP.NET based site you need to be aware of a potential attack that has been reported widely, known as the padding oracle exploit whereby encrypted values can be systematically decrypted or encrypted ultimately allowing an attacker to log into a forms authenticated site as a user of their choosing or download arbitrary files. Troy Hunt has an excellent write-up of the crypto stuff at the core of the attack.

There are some immediate things you can do that ScottGu outlines which involve updating <customErrors> in your web.config. But I felt that there are plenty of ASP.NET sites out that have their own way of dealing with errors which may be vulnerable due to the way they report errors.

One example is Exchange Outlook Web Access (Exchange 2007 at least), which by default exposes the underlying error details (helpfully including a stacktrace no less!) in its default error page and therefore can be potentially used as a padding oracle.

E.g. https://exchange.example.com/owa/auth/webresource.axd?d=foo:

image

This error page is served up as a 200 OK so it can’t be mitigated by a load balancer easily.

Whether an attack can actually be escalated against OWA from this is unknown to me at this time, nor which versions would be affected. OWA might not be using standard Forms Auth etc, but I haven’t heard the definitive word on this. Some kind soul has published steps on how to mitigate this apparent OWA vulnerability on the ASP.NET forums.

Anyway, I looked the attack against ASP.NET general and came up with what I thought was some useful information and posted this on the ASP.NET forums:

I've done some digging and come up with what I think is useful information for you if you have a custom error handling solution in place instead of or as well as the usual ASP.NET <customErrors> stuff. From comments on ScottGu's post it seem to be that the main suspect to be the actual padding oracle is WebResource.axd (possibly other axd's).
  • If you look in .NET Reflector at the IHttpHandler.ProcessRequest method in  System.Web.Handlers.AssemblyResourceLoader there's a call to Page.DecryptString early on.
  • This is the thing that will cause a HTTP 500 status to be returned if it fails, e.g. if the padding, etc in Request.QueryString["d"] is invalid
  • If the attacker manages to get the padding right, then it continues on, ultimately to call throw new HttpException(404...) It's this differentiation: is the padding correct (404) or not (500) that is at the root of the exploit: the padding oracle.
If your error handling returns exactly the same response for both - it masks the oracle. To test if you're vulnerable externally, a simple test is to request both:
  • webresource.axd?d=foo
  • webresource.axd with no parameters
and check using FiddlerTool, or FireBug that the entire response is identical for both including the status and all the headers (except for the Date header I guess!). Of course I may have missed something, but I hope this information helps you until the official fix comes out.

Using the script

To automate the above simple test I’ve written a little Windows Script Host script in JavaScript that can be used in a couple of ways:

  • double-click it and you will be prompted for a site URL
  • or invoke from a Command Prompt passing the site URL as the first argument. e.g.:
    cscript AspNetPaddingOracleDetector.js http://mysite.example.com

Here’s what it looks like if you point it at a site that may be vulnerable:

C:\>cscript //nologo AspNetPaddingOracleDetector.js http://mysite1.example.com
Testing site: http://mysite1.example.com/
MIGHT BE VULNERABLE: HTTP status mismatch

=== Response 1 ===
404 Not Found

=== Response 2 ===
500 Internal Server Error

And here’s what it look like if the site does not appear to be exposing an obvious padding oracle:

C:\>cscript //nologo AspNetPaddingOracleDetector.js http://mysite2.example.com/blah
Testing site: https://mysite2.example.com/blah/
Site might be OK: WebResource.axd is not acting as a padding oracle

Please use your judgement in interpreting the output and do not assume that you are safe/vulnerable based solely on its output.

To test Exchange Outlook Web Access typically you will want to run the script against the /owa/auth virtual directory, e.g. https://msexchange.example.com/owa/auth.

What about ScottGu’s script?

The difference between this script and the one mentioned by ScottGu is that this one actually does a simple test of your site from the outside to see if the mitigations you have put in place are likely to have helped you. For example you may have put an iRule on your F5 BigIP to mitigate the issue: this will help you test if that has been effective.

Plz send me teh codez

Download the script (right-click, save as) and just double-click it to try it out. Or view the script source on Google Code.

Hope that helps!

Follow @duncansmart on Twitter

16 Comments

If using Windows Live Writer or Microsoft Word 2007 to create blog posts in Windows SharePoint Services (WSS) 3.0 it appears, at least on our installation of SharePoint, that when you embed new images in the post, the image tags are generated with relative links, e.g. <img src="/Lists/Posts/Attachments/51/image_thumb.png"> . This is fine if you're viewing the feeds in a web browser as it can resolve the server name for the links from the feed address. But if you're using Outlook 2007 to subscribe to the feeds, any images you embedded in the post using Live Writer or Word are generated as relative links, Outlook can't/doesn't resolve the site name hence the images are broken:

image

Whose fault this is (Outlook/SharePoint/Live Writer) I don't know, but fixing it required a bit of coding hackery - I couldn't find any option in the SharePoint configuration for controlling the generation of RSS.

WSS v3.0's default RSS generator is a page called ListFeed.aspx that lives in Program Files\Common Files\Microsoft Shared\Web Server Extensions\12\template\layouts. By default it looks like this:

<%@ Assembly Name="Microsoft.SharePoint.ApplicationPages" %>
<%@ Page Language="C#" Inherits="Microsoft.SharePoint.ApplicationPages.ListFeed" %> 

Clearly all the logic is in Microsoft.SharePoint.ApplicationPages.dll. So to fix this, I backed up this file and added the following code to ListFeed.aspx which basically passes all of the feed content through a filter using HttpResponse.Filter which uses a regular expression to replaces any relative <img src="/blah"> tags with the absolute <img src="http://site/blah">.

<%@ Assembly Name="Microsoft.SharePoint.ApplicationPages" %>
<%@ Page Language="C#" Inherits="Microsoft.SharePoint.ApplicationPages.ListFeed" %>
<script language="C#" runat="server">
protected override void OnInit(EventArgs e)
{
  base.Response.Filter = new RelativePathFilter(base.Response.Filter, this.Request, this.Response);
  base.OnInit(e);
}

class RelativePathFilter : System.IO.Stream { System.IO.Stream _innerStream; string _siteUrl; Regex _imagesRegex; HttpResponse _response;

public RelativePathFilter(System.IO.Stream innerStream, HttpRequest request, HttpResponse response) { _innerStream = innerStream; _siteUrl = (new Uri(request.Url, HttpRuntime.AppDomainAppVirtualPath)).AbsoluteUri; _response = response;

// Finds rooted images &lt;img ... src="/ ...
_imagesRegex = new Regex(@"(&lt;img .*? \s src \s* = \s* [""']?)/", RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);

}

public override void Write(byte[] buffer, int offset, int count) { string content = _response.ContentEncoding.GetString(buffer, offset, count);

// Ignore the fact that it's theoretically possible for the &lt;img&gt; tag to be split between 2 Write()s
content = _imagesRegex.Replace(content, @"$1" + _siteUrl);

byte[] newBuffer = _response.ContentEncoding.GetBytes(content);
_innerStream.Write(newBuffer, 0, newBuffer.Length);

}

public override bool CanRead { get { return _innerStream.CanRead; } } public override bool CanSeek { get { return _innerStream.CanSeek; } } public override bool CanWrite { get { return _innerStream.CanWrite; } } public override void Flush() { _innerStream.Flush(); } public override long Length { get { return _innerStream.Length; } } public override long Position { get { return _innerStream.Position; } set { _innerStream.Position = value; } } public override int Read(byte[] buffer, int offset, int count) { return _innerStream.Read(buffer, offset, count); } public override long Seek(long offset, System.IO.SeekOrigin origin) { return _innerStream.Seek(offset, origin); } public override void SetLength(long value) { _innerStream.SetLength(value); } } </script>

I'm not entirely sure whether hacking around with these files is supported (although it did seem to survive the installation of WSS 3.0 SP1), or if there's a better way of doing this, so use at your own risk.

Update 22 April 2008: Modified code so that it doesn't assume the site is at the root of a virtual server.