Chaining Tricky OAuth Exploitation To Stored XSS

Hey everyone, hope you all are having a great 2019 so far.

I found this cool bug and wanted to share, so here it goes.

So after going through a private program on hackerone, i found a Self-XSS[AngularJS Template Injection] and a misconfigured OAuth implementation with low impacts. These 2 bugs alone wouldn’t have a great impact but if chained together, will lead to a Perfect Stored XSS!

Let’s call the application redacted.com for obvious reasons.

To give a background of redacted.com, it’s file storage service just like Google Drive or DropBox. User can upload, download, share their files from this platform.

I found XSS on the name of file being uploaded. So a file name like {{constructor.constructor(‘alert(1)’)()}}.jpg will result in XSS on the uploaded files dashboard. Cool! But it’s Self XSS.

There’s an easy way i found to make it Stored XSS on other users just by sharing the link to file and the file get’s imported in there uploaded files dashboard with the name unchanged, thus resulting in Stored XSS but i’ll show you one more interesting way i found to do this.

In settings, i found a feature to import your files from DropBox. To do that, you need to connect the application to your Dropbox account using OAuth.

I’ll quickly explain the OAuth flow of this application.

  1. So first, user clicks on connect Dropbox button and a GET request is initiated like this (https://dropbox.com/oauth2/authorize?client_id=***********&response_type=code&state=****************&redirect_uri=https%3A%2F%2Fwww.redacted.com%2Faccount%2Fsettings%2Fdropbox-callback).
  2. Then user is redirected to Dropbox, logins and click on Allow.

3. Just after clicking Allow, a GET Request containing the state parameter and auth_code is being sent to redacted.com as can be seen in Location header below.

4. As soon as this GET Request is executed on redacted.com, Dropbox get’s synced with current user session and all files from it can be imported to redacted.com.

So i started testing this OAuth flow. The goal was to somehow connect my Dropbox account to other redacted.com users but found that everything is in place.

The redirect_uri was perfectly whitelisted, state parameter was there as well, can’t use the auth_code twice etc. But still i tested the state parameter that whether the application was verifying it with current user session and found that it was verifying.

So, I can’t use the final URL coming from Dropbox (https://www.redacted.com/account/settings/dropbox-callback?state=********code=**********) on other redacted.com users account. Than what’s WRONG?

Out of curiosity i removed the state parameter from URL(https://www.redacted.com/account/settings/dropbox-callback?code=**********) and used it in different user account and surprisingly, my Dropbox account was connected to that user.

So basically i can connect my Dropbox account with anyone’s redacted.com account with just a GET request. But you might be wondering, what good is that? There wasn’t any option to login to redacted.com using Dropbox account so account takeover is not possible using it. But since i knew there’s already a XSS in file name, i took advantage of this OAuth misconfig.

EXPLOIT SCENARIO

  1. Uploaded a file with name {{constructor.constructor(‘alert(1)’)()}}.jpg on Dropbox(strangely it was allowed).
  2. Give the final OAuth URL without state parameter from Dropbox to victim (https://www.redacted.com/account/settings/dropbox-callback?code=**********)
  3. Victim’s gets connected to our Dropbox account and imports all the files from Dropbox including the malicious file.
  4. When user visits Uploaded File Dashboard, our XSS payload in file name will be executed

So although the server was verifying the state parameter with current user session, but it wasn’t verifying the presence of it.

We can assume the server logic to be something like :

if(isset($_GET['state'])){
if($_GET['state'] != current_user_state)
ACCESS DENIED
exit()
}
ACCESS GRANTED

That’s it, a tricky way to bypass OAuth implementation and achieving stored XSS in an interesting way.

TIMELINE

Jan 1,2019 — Report Submitted

Jan 2, 2019 — Triaged

Jan 8, 2019 — Resolved

Jan 15,2019 — Rewarded

If you found this post useful in anyway, make it useful for others as well by sharing. More coming.

And you can also hit me up on twitter for anything.