Preventing Denial of Service via Image Processor

Introduction#

A few weeks ago, a partner agency contacted me with some results of a routine penetration test on one of our mutual clients websites.

There were concerns that the ImageProcessor library that comes with Umbraco could be exploited in such as way that is could be possible fill the web servers storage by simply modifying a the query-string

The ASP.NET Image Processor library was found to cache generated images on the local disk based on the server response time differences. Specifically, serving requests for new images took significantly longer (e.g. 447ms) than those for previously queried images (e.g. 66ms). Then by manipulating the height and width arguments to the crop parameter to the Image Processor, it could be possible to create thousands of copies of the same image file.

The Solution#

It turns out the solution is quite simple. We can hook simply hook into the ValidatingRequest event and overwrite the query-string if the referrer Host doesn’t match the request host.

If someone is requesting a crop that has already been cached, we return that crop, but If someone attempts to adjust a crop, we return a 600x315 crop with a”Protected” watermark across the image.

using ImageProcessor.Web.HttpModules;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.SessionState;
// Denial of Service via Image Processor
// The ASP.NET Image Processor library was found to cache generated images on the local disk based on the server 
// response time differences. Specifically, serving requests for new images took significantly longer(e.g. 447ms) 
// than those for previously queried images(e.g. 66ms).Then by manipulating the height and width arguments to the
// crop parameter to the Image Processor, it could be possible to create thousands of copies of the same image file.
          
// https://www.site.co.uk/media/1001/image.jpg?mode=crop&width=10001&height=10001
// https://www.site.co.uk/media/1001/image.jpg?mode=crop&width=10002&height=10002
// https://www.site.co.uk/media/1001/image.jpg?mode=crop&width=10003&height=10003
namespace Umbraco.Web
{
    public class Global : UmbracoApplication
    {
        protected override void OnApplicationStarted(object sender, EventArgs e)
        {
            //throw new NotImplementedException();
            base.OnApplicationStarted(sender, e);
            ImageProcessingModule.ValidatingRequest += ImageProcessingModule_ValidatingRequest;
        }
        private void ImageProcessingModule_ValidatingRequest(object sender, ImageProcessor.Web.Helpers.ValidatingRequestEventArgs args)
        {
            //throw new NotImplementedException();
            if (!string.IsNullOrWhiteSpace(args.QueryString))
            {
                var Request = args.Context.Request;
                NameValueCollection queryCollection = HttpUtility.ParseQueryString(args.QueryString);
                if (Request.UrlReferrer == null || (Request.UrlReferrer != null && Request.UrlReferrer.Host != Request.Url.Host))
                {
                    args.QueryString = "?mode=crop&width=600&height=315&watermark=Protected&color=fff&fontsize=20&dropshadow=true&fontfamily=arial";
                }
            }
        }
    }
}

References#