News for hackers. Stuff that matters.
Re:Developers don't understand CORS by tgsovlerkhgsel on Wednesday July 10, 2019 @04:44PM (#20406334)
No.

- You visit evil.com

- Evil tries to make an HTTP request to bank.com/transfer.php

- The browser happily performs the request, authenticated with your cookies, and the bank, having a CSRF vulnerability, happily sends your money to the attacker.

- Since 'evil.com' and 'bank.com' are different origins, Browser refuses to provide the response to evil.com, but the attacker doesn't care, he got the money.

CORS allows you to relax these restrictions, not tighten them.

Now, bank doesn't like these attacks. So they make the legitimate application send an additional custom header, "X-Totally-Secure: true". Despite being a really bad idea, if (big if) the browser follows the standards, this prevents the attack:

- evil.com tries to make the HTTP request as before

- Browser lets it through, as before

- Bank rejects the request because it's missing the magic header

So the attacker adds the header to the request:

- evil.com tries to make a non-standard HTTP request to bank.com/transfer.php, with the header attached

- BECAUSE IT'S A NON-STANDARD REQUEST, browser asks bank.com (as you described, OPTIONS)

- Bank.com replies "wtf do you want I don't know what OPTIONS is"

- Browser refuses to make the request

Unfortunately, the bank forgot that they have a marketing department, that runs ournewbankapp.com, and shows your current balance in the fake screenshot of the app to show how awesome it is. And your bosses' bosses' boss has yelled at the IT department that rolled out the security measure to make it work again. They make ournewbankapp.com send the magic header (including access-control-allow-credentials), but now the OPTIONS request fails. So they teach the web server to respond with "everyone is allowed" (with "access-control-allow-origin: *" as you described) because they're lazy and dumb.

But because browser vendors know that developers are lazy and dumb, the browser completely ignores this: If access-control-allow-credentials is set, the allowed origin must be listed explicitly. The developers give in, and explicitly add ournewbankapp.com to the header, and now it works, but the attack doesn't work.

(part 2 follows)

Re:Developers don't understand CORS by tgsovlerkhgsel on Wednesday July 10, 2019 @05:05PM (#20406541)
Ways this story could have ended badly for the developers:

- instead of hardcoding the allowed value, they set it to always echo the value of the Origin header - browsers are powerless against that much stupidity, and can't distinguish it from a correctly configured server that is allowing the request. evil.com sends the request, bank says "evil.com" is allowed, browser shrugs and sends the request.

- instead of using CORS, they could have tried to build their own custom solution that allows ournewbankapp.com. The attacker would have to analyze how it works, and would then most likely find a way to exploit it to perform the attack, while legitimate users with privacy extensions would make the support hotline despair due to countless people complaining "I can't send money".

- instead of adding the custom header, they could have decided to check if "origin" is present, and if so, assume it's a cross-site request and check the origin against a whitelist. This still relies on standard-compliant browsers but isn't the worst idea AS LONG AS YOU REQUIRE AN ORIGIN VALUE, AND TREAT LACK OF AN ORIGIN HEADER AS A FATAL ERROR, BEFORE TAKING ANY ACTION (e.g. the money transfer) BASED ON THE REQUEST. I think can force same-origin requests to include the origin header with some option on fetch(). But if you treat a missing (or "null") header as ok, the attacker will likely find a way to make some browsers send no header, and steal money again. Sending "null" (or some other default value, been a while since I played with it) is easy, I think with some iframe trickery.

- they could have used plain HTTP, exposing them to DNS rebinding attacks even from remote attackers who cannot sniff their user's traffic.

- they could have an XSS vulnerability somewhere on the main site or the whitelisted marketing site, allowing the attacker to proxy his malicious requests through that site (and thus make them use a whitelisted origin). Maybe some long-forgotten kludge designed to proxy requests to third parties that don't support CORS...

Re:Developers don't understand CORS by aaronharnly on Wednesday July 10, 2019 @05:28PM (#20406757)
Variant of #1 that I've seen not infrequently:

They set it to check a regex for `bank.com` to also allow `subdomain.bank.com`, but inadvertently also allow `bank.com.evil.com`, `evilbank.com`, or similar.

Re:Developers don't understand CORS by Thorrez on Thursday July 11, 2019 @12:03AM (#20409020)
How would DNS rebinding work? I thought DNS rebinding only works if there's some sort of authority that's ambient across hostnames (such as whitelisted user IP ranges, or connectivity to a network), which doesn't seem to apply here. If you do have authority that's ambient across hostnames, my thought of how to protect yourself is to check the Host header.

XSS is essentially completely separate from CORS and CSRF. XSS is a bad vulnerability, worse than CSRF, and you can't expect any type of CSRF prevention to protect against XSS.

Re:Developers don't understand CORS by blauditore on Thursday July 11, 2019 @10:00AM (#20411541)
How would one handle the situation of having to respond to (authenticated) requests from a web app running on both localhost and a domain-based website?

I've been in this situation before with web-based cross-platform apps using Cordova. As far as I remember, I ended up echoing the origin domain if it matched a set of allowed domains (basically localhost and example.com), since regular expressions or lists are not supported in allowed-origin headers. For session tracking, I used tokens rather than cookies to avoid CSRF issues. Would there have been any better solution for this? I think not.

Re:Developers don't understand CORS by MrBuddyCasino on Thursday July 11, 2019 @01:03AM (#20409251)
This is the first comprehensible, and more importantly memoizable, description of CORS I have read. Hope it sticks this time.
Re:Developers don't understand CORS by aaronharnly on Wednesday July 10, 2019 @04:56PM (#20406467)
I know you know this, but to clarify steps 2/3:

- Evil tries to make an HTTP request to bank.com/transfer.php

If this is a regular HTTP POST (ie submitting a form, and the browser window changes location), the browser will allow it.

If this is an xhr POST, the browser, following the same-origin policy, allows the request, but prevents accessing the response (unless allowed with CORS).

[EDITED with tgsovlerkhgsel's help!]

Re:Developers don't understand CORS by tgsovlerkhgsel on Wednesday July 10, 2019 @05:15PM (#20406633)
Even a JavaScript initiated POST request will go through. Blocking it would not make a lot of sense, because the attacker could just use the FORM (possibly in an iframe to keep it invisible to the victim).

It is possible that XHR, or common XHR libraries, default to adding some header that makes it a non-standard request, but a fetch() call works.

In Firefox, open a debug console and run

    fetch('https://otherorigin.example.com', {method: 'POST', body: 'blah'})
You will see two things. In the console:

    Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://otherorigin.example.com/. (Reason: CORS request did not succeed).
In the network tab, a HTTP request.

Replace with a URL of a server you control, or run `nc -lnvp 9999` and replace the URL with http://127.0.0.1:9999, to see that the request is indeed being made.

As the author said... few people understand CORS.

Re:Developers don't understand CORS by avisser on Thursday July 11, 2019 @08:59AM (#20411136)
Bad naming strikes again? The message "Request Blocked" means something unambiguous to me. And it doesn't mean "I made the request, but won't let you see the response."
Re:Developers don't understand CORS by pixelperfect on Wednesday July 10, 2019 @05:53PM (#20406998)
But for XHR only POST requests that meet a lot of constraints (defined here https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#Simpl...) will work, right?

Nothing with a Cookie header for example...

Re:Developers don't understand CORS by Thorrez on Thursday July 11, 2019 @12:31AM (#20409141)
That page says that the Cookie header cannot be "manually set". Headers can still be automatically sent, and the browser can automatically send cookies from the cookie jar. So for example this will not send a request with the manually set cookie:

    r = new XMLHttpRequest();
    r.withCredentials = true;
    r.addEventListener('load', function(e) {console.log('loaded: ', this, e);});
    r.addEventListener('error', function(e) {console.log('error: ', this, e);});
    r.open('GET', 'https://www.google.com');
    r.setRequestHeader('Cookie', 'somecookie=somecookievalue')
    r.send();
But this will send a request with the automatically set cookies just fine:

    r = new XMLHttpRequest();
    r.withCredentials = true;
    r.addEventListener('load', function(e) {console.log('loaded: ', this, e);});
    r.addEventListener('error', function(e) {console.log('error: ', this, e);});
    r.open('GET', 'https://www.google.com');
    r.send();
Assuming the user is already logged in to bank.com , the user's cookies will be automatically sent on the request, and the transfer will go through assuming there is no CSRF protection.
Re:Developers don't understand CORS by joshschreuder on Thursday July 11, 2019 @03:46AM (#20409819)
TIL. Thank you for teaching me something - I had no idea the request still went through and it was just the response being blocked.
Re:Developers don't understand CORS by aaronharnly on Wednesday July 10, 2019 @05:23PM (#20406717)
Thank you, I stand corrected! Editing :-)
Re:Developers don't understand CORS by metafunctor on Wednesday July 10, 2019 @05:05PM (#20406547)
How hard is it to trigger a regular HTTP POST instead of an xhr POST?
Re:Developers don't understand CORS by Thorrez on Thursday July 11, 2019 @12:36AM (#20409164)
Not hard. Just make an html form with the values you want, then with javascript call

    document.getElementById("myForm").submit();
Re:Developers don't understand CORS by quickthrower2 on Wednesday July 10, 2019 @05:14PM (#20406621)
Another option is to use a secure token (https://github.com/OWASP/CheatSheetSeries/blob/master/cheats...) this is a PITA but frameworks like ASP.NET have this baked in so it's not really much of a hassle in practice.
Re:Developers don't understand CORS by superasn on Wednesday July 10, 2019 @04:56PM (#20406465)
It certainly won't allow me to make xhr POST requests without sending an OPTIONS request first. Are you sure about that?
Re:Developers don't understand CORS by tgsovlerkhgsel on Wednesday July 10, 2019 @05:15PM (#20406636)
Re:Developers don't understand CORS by superasn on Wednesday July 10, 2019 @07:21PM (#20407608)
Amazing. You learn something new everyday. Though it does make sense why blocking it won't serve any purpose as form submissions are going thru anyway.

Also glad I didn't mention jsonp. God knows what the devs at ournewbankingapp would do with that one ;)

Re:Developers don't understand CORS by giaour on Wednesday July 10, 2019 @05:13PM (#20406612)
Yes, you can send some POST requests without a preflight OPTIONS request: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#Simpl...
Re:Developers don't understand CORS by jchw on Wednesday July 10, 2019 @05:20PM (#20406696)
Actually they can do better than just a custom header with an XSRF token, which is fairly standard nowadays for requests that are authenticated via Session Cookies (Django for example strongly enforces this by default.)
Re:Developers don't understand CORS by tgsovlerkhgsel on Wednesday July 10, 2019 @05:38PM (#20406861)
Indeed, this is the canonical way of avoiding CSRF (= XSRF) attacks. I intentionally only explained the header method because I didn't want to confuse readers with another concept, and because it's a really nice example to explain CORS.

https://github.com/OWASP/CheatSheetSeries/blob/master/cheats... is probably one of the best documents discussing this, including the drawbacks of the header method that I used (and warned against) in my example:

https://github.com/OWASP/CheatSheetSeries/blob/master/cheats...

Re:Developers don't understand CORS by strictfp on Wednesday July 10, 2019 @06:02PM (#20407065)
Wait. If the resource at bank.com is protected by a CORS filter, the request which is issued at step three will have an origin header, and the CORS implementation is free to issue a 401 at that point. At least as far as I have understood. That would be tightening the restrictions. Am I missing something?
Re:Developers don't understand CORS by tgsovlerkhgsel on Wednesday July 10, 2019 @08:00PM (#20407852)
There used to be ways to get rid of the origin header, not sure if this is still the case. You can easily set it to "null", e.g. with some iframe trickery.
Re:Developers don't understand CORS by phdsolutions on Thursday July 11, 2019 @09:11AM (#20411210)
As others have mentioned - thanks for this! I thought I understood CORS, but one thing I apparently wasn't clear on was the conditions on which a pre-flight request was made. Thanks for clarifying!