Spectrum: Cross-site request forgery(CSRF) vulnerability

Created on 30 Jul 2018  路  14Comments  路  Source: withspectrum/spectrum

Severity : High

Description : Since there is no valid way of determining where the request to spectrum is originating from, an attacker can setup a malicious website and when the victim visits that page, the attacker can make any number of authenticated requests to spectrum as the victim.

Proof of Concept : Bug Demo Video

Reproduction Steps :

  1. Log into your spectrum.chat account and check your account setting like name and website.
  2. Visit https://devangeles.com/csrf-exploit.html and wait for 2 seconds.
  3. Go back to your settings and refresh(CTRL+F5 - no cache refresh), you'll see that your name, image, and other things have changed to something different(Nothing harmful for the sake of proof of concept, only basic settings change). If you didn't see the changes try clearing the cache and then reload.
  4. If you want to try it out locally, I've attached the exploit file(_csrf-exploit.txt_) down below, rename it to _whatever.html_ and open in a browser to see the exploit work.
    csrf-exploit.txt

Explanation :

  • Whenever there's a request made to the sever which updates something or does something which should only be done by an authenticated user, it's always necessary to have valid way of detecting the origin of the request or else a malicious website can send the same request and server thinks it came from your website, but it didn't.
  • Since a website can make requests to any other website, but cannot read data due to Same Origin Policy, CSRF attacks don't break this rules at all, since they only send the request and don't really care about the response.
  • Browser happily sends the cookie to the website whenever a request is made regardless of the origin. So there has to be a way to validate that the request came from the expected origin and we do that using random tokens. We generate a strong random token and send it as a part of the html form or something like that. When the request is made, we get back the same token and we validate if they are the same. If it was not the same token, then we drop the request.
  • In the case of spectrum, there was no CSRF tokens, which lets an attacker to simply repeat the request (captured by a web-proxy like burp suite) from a malicious domain as an AJAX request and make things happen.

Impact : An attacker can make any request to an endpoint in the context of a victim's session like delete the any account without authorization.

Mitigation : Find a good way to identify and validate the origin request, probably using randomized tokens. More on Mitigations

Extras

  • JSON CSRF : Since the endpoint needed a _multipart-form content-type_, it was easy to forge the request, but when it comes to a _JSON content-type_, it's a little different. We need to prepare somethings for this exploit to work. This basically works on spoofing the content-type via a Flash file(_.swf_) and then make a 307 redirect to the endpoint to make the CSRF work. If you want to try deleting the user account, this method is perfect because the content-type has to be _application/JSON_ and there's no _multipart-form_. I've also made a proof of concept for the JSON CSRF attack which you can find it here https://devangeles.com/JSONCT/. Open the exploit.html and allow flash to work. This exploit does not delete the user's account, I thought that would be really dangerous to try out, so instead, this proof of concept is for changing the name and website of you account settings. See references down below to know more about JSON CSRF.
    JSON CSRF Demo Video
  • Recently I reported a CORS misconfiguration bug to @brianlovin over an email, got some great feedback, Thanks for that, but anyways, If we pair these 2 exploits, which is CSRF + CORS misconfiguration, it's an Arbitrary READ (CORS) + Arbitrary WRITE (CSRF) of data to a victim's account and that's really dangerous. I hope the team fixes these issues quickly.

References

Have a great day!
s0cket7

FIX NOW

All 14 comments

I tried but was unable to reproduce using the steps provided. Chrome reports the xhr is blocked (as intended):

Access to XMLHttpRequest at 'https://spectrum.chat/api' from origin 'https://devangeles.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

Yes, it blocks, unless you have a domain like attacker-spectrum.chat (because there's also a CORS misconfiguration bug - I've reported it to @brianlovin over the email), but in CSRF the response doesn't really matter, only the request has to be processed in the backend. So even if you get the error, it would still work because the request will be processed in the backend regardless of the CORS.

I just tested the exploit in Chrome, it works. Yes I did get the same error but it still worked anyway because in CSRF attacks the reponse really doesn't matter, Same Origin Policy will act.

image

  1. Make sure you've logged into your account.
  2. Open a new tab and visit https://devangeles.com/csrf-exploit.html
  3. Go back to the settings and Ctrl+F5, for no cache reload. (maybe repeat this step a couple of times)

Thank you for the in-depth report, I'm taking a look at mitigating this!

My pleasure.

Also, could you open another issue for the CORS misconfiguration issue? I'd like to tackle that too.

Sure.

Another question just to make sure I understand everything: how does https://devangeles.com/csrf-exploit.html get the value of the cookie to send to the server?

Whenever the browser initiates the request to spectrum.chat it automatically sends the cookie, even if that request originated from a different origin like https://devangeles.com, so that's the reason the origin check is critical or else these kinds of attacks are possible.

3698 I've opened a new issue on CORS as requested.

Whenever the browser initiates the request to spectrum.chat it automatically sends the cookie, even if that request originated from a different origin like https://devangeles.com

I see, thanks for the elaboration!

As far as I can tell, setting the SameSite cookie attribute (#3697) and then checking the Origin and Referrer headers should be enough to combat this issue?

  • The Origin and Referrer check works everywhere, except in IE11
  • SameSite isn't supported cross-browser, but it does cover users on IE11, except for users who are on very old versions of IE11

So in summary, only a tiny subset of users who are on very old versions of IE11 would still be affected, but that should be it, if I understand that correctly?

Although checking for Origin and Referer header is kind of a solution to this problem, it's not really the right way because those headers are not always reliable (like in IE). So you have to choose a good and reliable way to ensure that it is protected from CSRF attacks. One way is by using randomized tokens, so basically on the server-side, you can generate a unpredictable random token and send it to the client as a part of html form hidden element or something like that and then every time there's a request from the client, you check for this token, if they match, you know that this request was from the know origin. With this kind of approach you can even protect those small subset of IE users.

You can check out more defenses to CSRF here

@mxstbr this article on universal react CSRF protection might be useful as a reference.

Unfortunately, we can't easily use the server-side token approach because our apps serve a client-side-only app shell via ServiceWorker caching鈥攚hich means after the first request, no request will hit the server apart from API fetches. (on top of that it would mean that any user who's currently logged in would be logged out since they don't have a CSRF token, but that's only a minor nuisance)

As far as I can tell, this only won't work in old versions of IE11 and before. According to GA ~0.5% of our users are on those versions of IE. That means the Origin and Referer check + samesite cookie get us a solid 99% of the way there, so let's go for that for now and then investigate how to do handle tokens properly with our caching setup in the future?

Sounds good.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

dmitryyankowski picture dmitryyankowski  路  4Comments

thundernixon picture thundernixon  路  4Comments

entrptaher picture entrptaher  路  3Comments

mxstbr picture mxstbr  路  6Comments

mxstbr picture mxstbr  路  5Comments