views:

239

answers:

4

This question is somewhat of a follow up to How serious is this new ASP.NET security vulnerability and how can I workaround it? So if my question seems to be broken read over this question and its accepted solution first and then take that into the context of my question.

Can someone explain why returning the same error page and same status code for custom errors matters? I find this to be immaterial especially if this is advocated as part of the work around to it.

Isn't it just as easy for the script/application to execute this attack and not specifically care whether or not it gets a http status code and more on the outcome? Ie doing this 4000 times you get redirected to an error page where on 4001 you stay on the same page because it didn't invalidate the padding?

I see why adding the delay to the error page is somewhat relevant but doesn't this also just add another layer to fool the script into thinking the site is an invalid target?

What could be done to prevent this if the script takes into account that since the site is asp.net it's running the AES encryption that it ignores the timing of error pages and watches the redirection or lack of redirection as the response vector? If a script does this will that mean there's NO WAY to stop it?

Edit: I accept the timing attack reduction but the error page part is what really seems bogus. This attack vector puts their data into viewstate. There's only 2 cases. Pass. Fail.

Either Fail, they're on a page and the viewstate does not contain their data. No matter what you do here there is no way to remove the fail case because the page just will never contain their inserted data unless they successfully cracked the key. This is why I can't justify the custom errors usage having ANY EFFECT AT ALL.

Or Pass, they're on a page and the viewstate contains their inserted data.

Summary of this vulnerability


The cipher key from the WebResoure.axd / ScriptResource.axd is taken and the first guess of the validation key is used to generate a value of potential key with the ciphered text.

This value is passed to the WebResource.axd / ScriptResource.axd at this point if the decryption key was guessed correctly their response will be accepted but since the data is garbage that it's looking for the WebResource.axd / ScriptResource.axd will return a 404 error.

If the decryption key was not successfully guessed it will get a 500 error for the padding invalid exception. At this point the attack application knows to increment the potential decryption key value and try again repeating until it finds the first successful 404 from the WebResource.axd / ScriptResource.axd

After having successfully deduced the decryption key this can be used to exploit the site to find the actual machine key.

+1  A: 

No, it isn't a big lie. See this answer in the question you referenced for a good explanation.

Wyatt Barnett
That lends weight to GvS's answer however that makes no justification on why having custom 404, 500 returning correct status codes means anything.
Chris Marisic
Will DV soon unless additional information is added since this really just dupes GvS's answer and without the statements GvS makes the response you link doesn't imply what GvS said much more clearly.
Chris Marisic
+1  A: 

The workaround works because:

  • You do not give any indication about "how far" the slightly adjusted took you. If you get another error message, that is information you can learn from.
  • With the delay you hide how long the actual calculation took. So you do not get information, that shows if you got deeper into the system, that you can learn from.
GvS
I can accept the timing justification now, since you're using that to hide whether you're getting a legitimate application error because the app expected something else in viewstate and got a cast exception or format exception etc.
Chris Marisic
+5  A: 

I am going to elaborate on my answer in the thread you referenced.

To pull off the attack, the application must respond in three distinct ways. Those three distinct ways can be anything - status codes, different html content, different response times, redirects, or whatever creative way you can think of.

I'll repeat again - the attacker should be able to identify three distinct responses without making any mistake, otherwise the attack won't work.

Now coming to the proposed solution. It works, because it reduces the three outcomes to just two. How does it do that? The catch-all error page makes the status code/html/redirect all look identical. The random delay makes it impossible to distinguish between one or the other solely on the basis of time.

So, its not a lie, it does work as advertised.


EDIT : You are mixing things up with brute force attack. There is always going to be a pass/fail response from the server, and you are right it can't be prevented. But for an attacker to use that information to his advantage will take decades and billions of requests to your server.

The attack that is being discussed allows the attacker to reduce those billions of requests into a few thousands. This is possible because of the 3 distinct response states. The workaround being proposed reduces this back to a brute-force attack, which is unlikely to succeed.

sri
But that's not the case. The error page itself no matter what does not reduce the results because you're on Page1, error occurs you're on Page2. No matter what tom foolery you do with Page2 it IS DIFFERENT from Page1, it doesn't matter how elaborate you get it, it's still trivially obvious you're on an an error page even if it returns a 200 status code. Even if you use server transfer to avoid a true direct the content of the page2 is entirely different than Page1, how does it in anyway matter whether the content is 200, 404 or 500?
Chris Marisic
@Chris - Outcome 1 : No error, you stay on page 1. Outcome 2: There is an application error, you are on page 2. Outcome 3: There is a cryptographic error, you are *again* on page 2. From the attacker's perspective, he is either sees Page 1 or Page 2. There is no third outcome, and so your application is safe.
sri
Outcome 3 is a none issue, if the script is attempting to hack the view state it would make sense to always assume if I don't get outcome 1 no matter what just try again. And only stop after a few hundred thousand or million attempts to give up, or some time based factor.
Chris Marisic
@Chris: But "just trying again" will require millions upon millions of brute-force attempts. Being able to distinguish between outcome 2 and outcome 3 is, presumably, what allows the attacker to reduce an unfeasible attack to just a few (tens of?) thousands of focused attempts.
LukeH
@Chris - What you are describing is a brute force attack, and will take years/decades and billions of requests to actually arrive at outcome 1. The attack that everybody is discussing isn't brute force. It needs a few thousand requests to *accurately* decrypt the view state, and then another few thousand to encrypt whatever data is desired. We are speaking of 30 minutes v/s a few decades. LOT of difference.
sri
Then what you're stating once again validates my statements that hiding the 500 error with a 200 status code doesn't matter because it still doesn't let the attacker specifically know it's the cryptographic error. I still say this is a non issue because they KNOW it's 99.99% likely to be the cryptographic error.
Chris Marisic
I never said anything about hiding 500 error with 200. It really is about reducing 3 distinct outcomes to 2 distinct outcomes. I am unable to understand your concern.
sri
Scott Gu's blog along with the original question on stackoverflow that spawned my question all say that you need to disable all custom error flavors so no more 401, 403, 404, 500 etc and replace them all with specific generic error page and then fix that to return status code 200 instead of any error code.
Chris Marisic
Scott Gu's blog says `always return the same error page`. It does not say that error page should always return 200 status code; it could return a 500 for all you care. As long as you ensure that the error page demonstrates a consistent behaviour no matter what the exception is, you are okay.
sri
eglasius
+5  A: 

re:

I don't think you've added anything to this thread except reiterate what I've said. "about making sure the attacker can't determine if the request failed because it couldn't decrypt /padding was invalid, vs. because the decrypted data was garbage." How does this have relevance on whether they're redirected to a 200, 404 or 500? No one can answer this, this is the fundamental question. Which is why I call shenanigans on needing to do this tom foolery with the custom errors returning a 200. It just needs to return the same 500 page for both errors.

I don't think that was clear from the original question, I'll address it:

who said the errors need to return 200? that's wrong, you just need all the errors to return the same code, making all errors return 500 would work as well. The config proposed as a work around just happened to use 200.

If you don't do the workaround (even if its your own version that always returns 500), you will see 404 vs. 500 differences. That is particularly truth in webresource.axd and scriptresource.axd, since the invalid data decrypted is a missing resource / 404.

Just because you don't know which feature had the issue, doesn't mean there aren't features in asp.net that give different response codes in different scenarios that relate to padding vs. invalid data. Personally, I can't be sure if there is any other feature that gives different response code as well, I just can tell you those 2 do.


Can someone explain why returning the same error page and same status code for custom errors matters? I find this to be immaterial especially if this is advocated as part of the work around to it.

Sri already answered that very clearly in the question you linked to.

Its not about hiding than an error occurred, is about making sure the attacker can't tell the different between errors. Specifically is about making sure the attacker can't determine if the request failed because it couldn't decrypt /padding was invalid, vs. because the decrypted data was garbage.

You could argue: well but I can make sure it isn't garbage to the app. Sure, but you'd need to find a mechanism in the app that allows you to do that, and the way the attack works you Always need at least a tiny bit of garbage in the in message. Consider these:

  • ScriptResource and WebResource both throw, so custom error hides it.
  • View state is by default Not encrypted, so by default its Not involved the attack vector. If you go through the trouble of turning the encryption on, then you very likely set it to sign / validate it. When that's the case, the failure to decrypt vs. the failure to validate is the same, so the attacker again can't know.
  • Auth ticket also signs, so its like the view state scenario
  • Session cookies aren't encrypted, so its irrelevant

I posted on my blog how the attack is getting so far like to be able to forge authentication cookies.

Isn't it just as easy for the script/application to execute this attack and not specifically care whether or not it gets a http status code and more on the outcome? Ie doing this 4000 times you get redirected to an error page where on 4001 you stay on the same page because it didn't invalidate the padding?

As mentioned above, you need to find a mechanism that behaves that way i.e. decrypted garbage stays on the same page instead of throwing an exception / and thus getting you to the same error page.

Either Fail, they're on a page and the viewstate does not contain their data. No matter what you do here there is no way to remove the fail case because the page just will never contain their inserted data unless they successfully cracked the key. This is why I can't justify the custom errors usage having ANY EFFECT AT ALL.

Or Pass, they're on a page and the viewstate contains their inserted data.

Read what I mentioned about the view state above. Also note that the ability to more accurately re-encrypt is gained After they gained the ability to decrypt. That said, as mentioned above, by default view state is not that way, and when its on its usually accompanied with signature/validation.

eglasius
I don't think you've added anything to this thread except reiterate what I've said. "about making sure the attacker can't determine if the request failed because it couldn't decrypt /padding was invalid, vs. because the decrypted data was garbage." How does this have relevance on whether they're redirected to a 200, 404 or 500? No one can answer this, this is the fundamental question. Which is why I call shenanigans on needing to do this tom foolery with the custom errors returning a 200. It just needs to return the same 500 page for both errors.
Chris Marisic
@Chris see update in my answer.
eglasius
Now this finally makes any sense. To my knowledge not a single resource I've read has mentioned that the cause of why you need to disable specific custom error pages was to prevent the web resource.axd from returning a 404 error for a request that validates but a resource doesn't exist.
Chris Marisic
Did I get everything right in my answer summary? Or did I reverse the machine key and validation key?
Chris Marisic