HTTP Request Smuggling - ERRATA (the IIS 48K buffer phenomenon)

From: Amit Klein (AKsecurity) (aksecurity_at_hotpop.com)
Date: 09/22/05

  • Next message: Martin Schulze: "[SECURITY] [DSA 817-1] New python2.2 packages fix arbitrary code execution"
    Date: Thu, 22 Sep 2005 13:40:11 +0200
    To: bugTraq <bugtraq@securityfocus.com>
    
    

    Hi

    With respect to the IIS/5.0 48K "bug" (see "HTTP Request Smuggling",
    http://www.watchfire.com/resources/HTTP-Request-Smuggling.pdf, pages 6-7 or 4-5),
    Noam Ben-Yochanan commented that IIS/5.x provides with the programmer with a way to consume
    the request body (beyond the 48K usually read), thus disabling the HTTP Request Smuggling
    caused by this anomaly. Noam sent me a link that explains this:

    Microsoft Knowledge Base article #810957:
    http://support.microsoft.com/default.aspx?scid=kb;en-us;810957

    The short story is that IIS reads the body in buffers of 48KB (the default
    value of UploadReadAheadSize in the MetaBase registry). It probably always reads
    the first buffer.
    Now, the HTTP Request Smuggling trick works by sending any Content-Type
    OTHER THAN application/x-www-form-urlencoded. This means that IIS has nobody parsing
    the data and consuming more data, so it stops reading after those 48K, and whatever
    comes next is considered the next request (while MS says it is "by design", in my
    opinion this is still a bug, and with security implications).

    As Noam noted, it is possible to "artificially" consume the data using
    Request.BinaryRead, e.g. something like:

      Dim data
      data = Request.BinaryRead(Request.TotalBytes)

    And by this, restore order.

    So a possible solution is to insert the above code in every ASP page.

    However, there is a serious downside to this method: if a page accesses
    Request.BinaryRead, it cannot access the Form collection (see the documentation of
    BinaryRead in
    http://msdn.microsoft.com/library/default.asp?url=/library/en-us/iissdk/html/c84b1f88-1a8a-
    4370-95de-e907f7fdd5e7.asp).

    Here's an improved solution. Note that the Content-Type of the request
    must be anything other than application/x-www-form-urlencoded for the attack to work (I
    understand now that this is due to the fact that in application/x-www-form-urlencoded, the
    IIS ASP.dll consumes the request body in fullness). So if a script wants to secure itself,
    it should check whether the Content-Type (accessible via
    ServerVariables("HTTP_CONTENT_TYPE") is application/x-www-form-urlencoded.
    If it is, then the script can proceed as usual, and access the data through the
    Request.Form collection. If the Content-Type is not
    "application/x-www-form-urlencoded", then the script
    should consume the data (as in the above example), and log the event
    (since this is probably an attack). Ideally, the script would terminate the TCP
    connection, but I don't know whether a script can terminate the TCP connection in a
    reliable manner.
    Perhaps sending a "Connection: Close" header can do the trick (or a 400
    status code?).

    So the proposed solution would be something like (pardon me for any ASP syntax error):

    If (Request.ServerVariables("HTTP_CONTENT_TYPE") <> _
    "application/x-www-form-urlencoded") Then
            ' Log a security error condition
            ' Consume leftover data
            Dim data
            data = Request.BinaryRead(Request.TotalBytes)
            ' Exit gracefully - do not proceed to normal flow
    EndIf
    ' Work as usual, can access Request.Form

    Still, it requires every ASP page to embed this kind of code. I think this
    makes the solution infeasible. And frankly I still call this a bug (be it by design or
    not) - an ASP page should not worry about its content-type or consuming the request body.

    Thanks again to Noam!
    -Amit


  • Next message: Martin Schulze: "[SECURITY] [DSA 817-1] New python2.2 packages fix arbitrary code execution"

    Relevant Pages

    • Re: XML Deserializing issue
      ... generating proxy and consume it. ... You can send feedback directly to my manager at: ... Here is the url to the web service. ... And the request input. ...
      (microsoft.public.dotnet.framework.aspnet.webservices)
    • Re: Checking File Size Before Upload
      ... be sent after the request body has been fully received ... HTTP response message. ... allow a client that is sending a request message with a request body to ...
      (comp.lang.php)
    • Re: Checking File Size Before Upload
      ... be sent after the request body has been fully received ... HTTP response message. ... allow a client that is sending a request message with a request body to ...
      (comp.lang.php)
    • XMLException issue Using WSE 2.0 SP3
      ... I am attempting to consume a web service using service pack 3, ... running into trouble while sending the request. ... An unhandled exception was generated during the execution of the current web ... Someone else suggested that I try consume the service using HttpWebRequest ...
      (microsoft.public.dotnet.framework.webservices.enhancements)
    • Re: XMLHttpRequest
      ... This is nonsense because you do not really use `updated' later. ... Assigning t_area.value is equivalent, however you probably wanted ... request which is evaluated _server-side_. ... not provide a property to access the request body later. ...
      (comp.lang.javascript)