Some Cross-Site Scripting (XSS) vectors arise from strict but allowed possibilities, forming tricky combinations. It’s all about contexts and sometimes the interaction between different contexts with different filters lead to some interesting bypasses.
So in order to understand how filters can be bypassed in some particular, multi injection scenarios, let’s start with an exercise/challenge tweeted some time ago.
— Brute Logic (@brutelogic) February 16, 2020
URL is here and full source code follows:
Besides filtering there’s also a WAF (Web Application Firewall) to make it a little harder to pop the alert box.
There are filters both in HTML and JS contexts.
Alone, they can do their filtering job perfectly: the first one, on input tag, makes it possible to break out of it, but no valid XSS vector can be built since it scrapes the “=” sign needed for almost all HTML-based XSS vectors. It also scrapes the SCRIPT tag in a case insensitive manner (notice the str_ireplace PHP function), the only remaining vector that does not require the equal sign.
The second filter makes sure none of the 2 ways to break out from a JS string value work. Greater than sign is replaced by its HTML entity (not allowing </script> breakout) as well as single quote (not allowing string delimiter breakout).
But together they open an avenue to bypass based on SVG tag. SVG is XML-based markup language for describing two-dimensional based vector graphics and browsers do some kind of “double decoding” with HTML entities inside them. A scheme like <svg>[some tags]<script>[encoded code]</script> works fine .
So solution comes in a form of a multi-injection vector which appears in both places, breaking out from input tag to open a <svg> tag and forcing a similar delimiter breakout technique on JS code.
It’s a combo of the following ones:
But WAF blocks it:
Bypassing JSON Encode
We start with a simple and straightforward case, again filtering both entry points properly.
URL is here.
The trick here is to use the fact that inside JSON encoding, a proper HTML tag is possible:
First filter doesn’t make possible to open a HTML tag to start a HTML-based XSS vector but it allows HTML comments.
With that in mind, to XSS this all we need is the following payload:
Which comments all the code down to script block which is not a script block anymore since the script tag is under comments.
Here is a different scenario which requires a similar trick:
URL is here.
An inline injection with some event handler “on[anything]” is not possible neither a tag breakout to inject comments like in previous scenario. So the way to XSS this is to use a malformed arbitrary HTML attribute:
Which works pretty much like in our previous case.
A slight variation of that case can be seen here.