XSS by Javascript Overriding

This post describes an interesting XSS that I recently encountered. I'll describe my thought process during the exploitation of this vulnerability as well.

Shortly after taking part in H1-702 (Hackerone's latest Live Hacking Event at time of writing), I was sitting in my room in the Luxor Hotel in Las Vegas, when I got a message from Emperor asking if I wanted to hang out. We were both caught in the purgatory between the end of H1-702 and the start of Def Con, so we decided to meet up and have a chat over coffee. So, sitting in the Excalibur Hotel next to the Luxor, we started hacking on separate targets.

While browsing my chosen target, I noticed a portion of Javascript in which there was a reflection taking place in two parts without sanitising user input.

function EatBanana(param){
	
    window.opener.document.getElementById('REFLECTION') = param;
    window.close();
    window.opener.document.getElementById('REFLECTION').focus();
    
}

The obvious thing to do here was to try and inject single quotes to escape the context, and insert a function like print() to perform XSS. Here's the problem; window.opener was null. This would cause the execution to halt before executing our malicious payload. Secondly, EatBanana() only executes when the user clicks on a particular button, so any XSS would be 1-click, reducing the impact.

To circumvent this, we need to leverage overriding. Overriding, per that blog post (since the Mozilla documentation was uncooperative), is defined as follows:

In the case of declaring two functions with the same name and parameters, JavaScript considers the latest function while interpreting.

This is great! We can use this to override EatBanana() to suppress the window.opener error and execute our Javascript. I did not, however, read that blog post at the time. What actually happened was that I had the idea to re-define the function to suppress the error so I tested it - and to my surprise, it actually worked. Javascript preferred the latest definition. It was at that point that I discovered that this was called overriding. So what does our payload look like? We just need it to close the function syntax since the two reflections are very similar in context, so fixing one will fix the other.

');} function EatBanana(param){print();('

This closes our previous declaration, and declares it again such that clicking the button will execute print(); and then ('').focus(); instead. But wait! This is still 1-click. What if we put our Javascript between the function declarations instead?

');} print(); function EatBanana(param){print();('

And it works! This is now 0-click XSS. Our final JS looks like this:

function EatBanana(param){
	window.opener.document.getElementById('');
 } 

print(); 

function EatBanana(param){
    print();
    ('').value = param; 
    window.close();
    window.opener.document.getElementById('');
} 

print(); 

function EatBanana(param){
    print();
    ('').focus(); 
}

This way, the function error is suppressed (although it will still error due to the focus() function once EatBanana is called but that doesn't affect our payload).

Emperor witnessed me jump in the air in delight once I got this payload working. I hope you learned something from this writeup! Until next time.

Edit 1: Yes, this payload also works:

');} print();{('

but this did not occur to me at the time until after I re-declared the function EatBanana.  It's also less elegant.

Edit 2: This also works:

');} print();{//

but my point stands.