Escalating Self-XSS for Riches
I have a bit of a love-hate relationship with Self-XSS. I find them somewhat regularly, and while I do think programs should pay a bounty for them (the lowest they offer), I’m often a bit frustrated if I can’t escalate them. I’ll almost always find them in an application where no other XSS is to be found, but there is almost always some other easily XSS-exploitable feature e.g. tokens in local/session storage or a plugin upload for RCE, etc.
Recently, however, I found two self-XSSs in two different bug bounty programs and was able to escalate them to High/P2 for $1,000 and $3,000 bounties.
App 1 – Self-XSS > IDOR > ATO
In this app, a user is able to create a report containing data they provide. I actually got a critical (RCE) via this feature about five years ago, but that is beside the point. I recently revisited the application. I noticed, again, that we were able to supply user input for the title of this report, and after some other minor tweaks I was able to get a working XSS payload.
The problem is the report is only for personal usage. Nobody else views these reports. The report is viewable at a link like so https://example.com/reports/{uuid}
And, as it turns out, this endpoint is IDOR-able. It’s a UUID-based IDOR, which I did submit as another low-finding, but since it’s an IDOR, I could simply send a victim the link to my report with XSS in the title. When they visit the link the XSS fires and steals their session token from local storage leading to ATO.
App 2 – Self-XSS > Blind Admin XSS
In a second application, I found a self-xss a year or two ago in a user’s email address like so.
1
"><svg/onload=confirm(1)>"@gmail.com
Then randomly, one day, it popped into my head that I never really looked at the JavaScript on this page. After taking a look at it, I noticed numerous administrator endpoints. When I saw these, the first thing I tried was to hit these unauthenticated, but that didn’t work 😭.
Here are some examples of these endpoints:
1
2
adminroutepath/search/user
adminroutepath/update/user
And then, there was an even more ominous function in the JS we could call – wxyz.impersonateThisUser(‘userID’)
. This implies that an admin can impersonate my user, and likely be subjected to my now not a self-XSS.
Anyway, at this point, you know that this app has admin functionality integrated into the same instance. Often, something I bring up to devs is to separate out admin functionality and put that only internally facing. That way a random user cannot see the JavaScript with admin functionality in it, which would make it very, very hard for them to force some sort of malicious action on the admin. Or, even if they stole auth tokens, they cannot access the application to use them.
But I digress. In this case, the XSS was limited to 80 characters, with some other character restrictions that I can’t remember (like no $ or ‘). Also, the CSP in place was fairly robust. Though, it did allow jsdelivr.com
, which makes it very broken.
CSP Bypass with JSDelivr
If your CSP allows JSDelivr, it is broken. A malicious actor can create a GitHub repo with arbitrary JS, paste the link in at https://www.jsdelivr.com/github
and get a new JS Delivr link which will deliver your JS from jsdelivr, bypassing the CSP.
However, when it came to this vuln, to fully prove it’s vulnerable, I’d need to get an admin to view my account, or impersonate me, or something along those lines. In these cases, you can simply put in a bunch of support requests or message support via chat or email or whatever means necessary, and hope they get hit by the XSS. In this case, since I didn’t want to do that if it wasn’t necessary, I explained this to the employee that was responding to my report, and they did some work behind the scenes to verify it was an issue and award me the bounty.
A special shoutout to this company triager who shall remain nameless!
Why Self-XSS is a Bug
We’ve all seen the conversations over UUID-based IDORs and if they are valid bugs for a bug bounty program. IMO they obviously are, but should get paid as a low, unless there is a UUID oracle of some sort. And I guess BugCrowd agrees, because their auto-severity-populator rates them as a Low.
But what about self-XSS? If we are calling a UUID-based IDOR a low, a self-XSS should also be a low without any proof of larger impact. It has potential for impact sometimes days later and the tester won’t even know it happened. Lots of times they’ll fire in privileged environments (blind xss) by an admin or employee viewing an affected page.
Or simply, if every other user controlled input is sanitized, why not this one? Do I think a tester deserves a critical without showing deeper exploitation, no, but they definitely deserve a low.