Challenge

The Buggy .Net challenge

Initial reckoning

The web page shows a POST form that has a single text field called filename:

Main page of Buggy .Net service

HTTP headers reveal that the server is a Microsoft-IIS/10.0 with ASP.NET 4.0.30319:

HTTP/1.1 200 OK
Cache-Control: private
Content-Type: text/html; charset=utf-8
Server: Microsoft-IIS/10.0
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
[...]

Using “Default.aspx” as the file name in the form retrieves the page source 😁 … but wait, the source is available at http://52.197.162.211/Default.txt anyways. Also, using “Default.txt” as the file name in the form retrieves that file. And obviously we cannot access the flag at neither “C:\FLAG.txt” nor “..\..\FLAG.txt” 🙀.

Source code

bool isBad = false;
try {
    if ( Request.Form["filename"] != null ) {
        isBad = Request.Form["filename"].Contains("..") == true;
    }
} catch (Exception ex) {
    
} 

try {
    if (!isBad) {
        Response.Write(System.IO.File.ReadAllText(@"C:\inetpub\wwwroot\" + Request.Form["filename"]));
    }
} catch (Exception ex) {

}

Interesting observations

My first though was something like “wtf? two separate try-catch-blocks?” Can we use that for anything evil? Certainly isBad will remain false if the first exception handler kicks in.

So how could we raise an exception in the first try-catch-block in order to permit path traversal without raising an exception in the second try-catch-block?

  • Can String.Contains raise an exception?
  • Can Request.Form[...] return anything that does not have a Contains() member method but still concatenates as useful string later on?
  • Can Request.Form[...] raise an exception in some cases (particularly on first invocation) but not in others?

Known bugs in ASP.NET 4.0 regarding request fields/forms?

So lets ask Google if there are any known bugs in ASP.NET …

Interesting, request validation has changed in ASP.NET 4.0 (right, that’s the version of our attack target!). This seems to have resulted in many complaints!

That did not help so far … but then I stumbled upon this: There’s actually someone* telling us that it might not be a good idea to ignore the exception that’s thrown on the first access to the Request collections.

*) NCC Group is certainly not just someone, but to be honest, I did not really care who’s exploit I could recycle back then 🙊.

Basic idea

The basic idea of that vulnerability is that, for POST requests, request validation prevents “dangerous content” (e.g. HTML tags or similar, such as <x) in POST form fields by terminating the whole application. However, the same content in query-string fields will pass initial request validation and will “only” raise an exception on first access of Request.QueryString[...] (since that field is populated on first access?)

Similarly, for GET requests, request validation prevents “dangerous content” (e.g. HTML tags or similar, such as <x) in GET query-string fields by terminating the whole application. However, the same content in form fields (i.e. in a request body encoded as application/x-www-form-urlencoded) will pass initial request validation and will “only” raise an exception on first access of Request.Form[...] (again, since that field is populated on first access?)

Nevertheless, query-string fields in a POST request are accessbile through Request.QueryString[...] and form fields submitted in the request body of a GET request (with content-type application/x-www-form-urlencoded) are accessible through Request.Form[...].

Hence, we should be able to successfully submit the form by the sending a GET request without any query-string field but with the filename field in the request body. Further, by also including another form field in the request body that will trigger that “late” request validation bug (or is it a feature if Microsoft declared to won’t fix? 😜), e.g. a simple &o=<x, we should be able to trigger an exception on first access of Request.Form["filename"] … and this is exactly what we need to escape from the first try-catch-block before changing isBad.

Exploit

GET / HTTP/1.1
Host: 52.197.162.211
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 42
Referer: http://52.197.162.211/

filename=%2E%2E%5C%2E%2E%5CFLAG.txt&o=%3Cx


Get the flag

The ASP.NET application now happily reads and prints the flag for us 😍:

hitcon{Amazing!!! @irsdl 1s ind33d the .Net KING!!!}

Final note (particularly to myself 😉)

Let your team mates proof-read your exploits!

Certainly that could have saved me from hours of (w)racking my brains why that exploit would fail even if everything tells me it must work like that 🙀.