Journeys in Quoteless and Multi Reflection XSS

Cross Site Scripting is a tricky bug to fix and bypasses for these fixes can be even trickier. While there are several ways to remediate or prevent XSS, I want to focus on HTML Entity Encoding and the contextual pitfalls that can occur with this method.

9 times out of 10, HTML entity encoding is going to block your attempts at popping an XSS. The app will render < > & " ' as HTML entities &lt; &gt; &amp; &quot; &apos;and make the reflected content safe and inert.

However, if you ever see this behavior occurring within a Javascript context, this means the HTML encoding solution is being used throughout the page without ensuring that content is being properly encoded for its context. With a well placed backslash and a bit of luck, XSS is sometimes possible without quotes or brackets.

Let’s take a look at an example:

http://example.com/page?q=test123%27%22%3c

Instead of appropriately encoding or escaping in this context (i.e. test123\'\"\x3C) HTML entities are used instead. By adding a backslash, we may be able to break the string and trigger an error.

http://example.com/page?q=test123%27%22%3c%5c

So now, we know it’s possible to break the context of this Javascript string because the injected backslash isn’t escaped. We’re escaping the double quote which essentially leaves us with an unquoted string. There’s not much we can do here, but this is where the luck aspect comes into play. If you can find…

  • Two reflected parameters

…Then XSS is likely possible, despite the protections in place! Here’s a real life example of this happening:

https://example.com/savings/thankyou?id=abc%27%3c&num=123%27%3c

Unfortunately, we can’t pop an easy XSS with </script><script>alert(1)</script> or '-alert(1)-' but can we work with this!

We can inject a backslash in the id parameter to break out of our string context. By escaping the quote, the value of id in the code is 'abc\', num: '

With our second parameter, num, we can take advantage of the newly created string and append a Javascript payload into this “unquoted” context:

https://example.com/savings/thankyou?id=abc%5C&num=};confirm(1);//

Dangit! The reflection context here is a a bit more complex to break out of. We need to break out a string, object, function call, and function definition in order to execute a Javascript payload. Using some trial and error, I ended up with a working payload.

id is simply \ and the payload for num is now this:

-1}});}; alert(1); {$.ajax({//

https://example.com/savings/thankyou?num=-1}});};+alert(1);+{$.ajax({//&id=abc%5C

B00M! XSS 😎

Here’s the step by step explanation from my original Hackerone report:

  1. By adding the \ in id,

I got a $250 bounty for this submission.

Keep your eyes open for weird encoding behaviors and multi-reflections! Param Miner and Reflection are great Burp Extensions to help when trying to exploit an XSS like this one.

If you’re interested in learning more about XSS, come join the Bounty Hunters Discord! https://discord.gg/bugbounty

Lastly, huge shout out to Brute Logic! His blog posts on quoteless and multi-reflection XSS gave me the skills and inspiration I needed for finding and exploiting this bug: https://brutelogic.com.br/blog/multi-reflection-xss/

Hi! I'm Ben, a Web App Hacker, Bug Bounty Hunter, and Self-Taught Techno Entomologist