2

While making a website I made an interesting mistake, and I'm wondering if this could be used to achieve XSS. I've come very close, but not quite.
I can put my user input into a string inside of a <script> tag. It looks something like this:

<script>
// some code above
some_variable = "my user input"
// some code below
</script>

I intended to escape " and \ characters, by putting a backslash in front of them. I also completely remove any newlines. But accidentally I put a normal slash instead of a backslash. So I only escaped the " and / characters.
This means I can escape the string with something like \" which will turn into \\" and break me out of the string.
Normally you would simply do \"-alert(1)// to escape the string, execute the alert and then comment out the rest of the code. But since in my unique example I escaped the / character, you can't comment out the rest with that.
To make it more clear here is an example:

// Common example
some_variable = "\\"-alert(1)//"
// My situation
some_variable = "\\"-alert(1)\/\/"

Lots of things I've tried already all result in some sort of syntax error, which doesn't execute the code. I'm wondering if there is some sort of trick I missed to still get XSS from this.

This question is similar to XSS inside JavaScript string literal without single quotes?, which isn't exploitable. But here I can actually get out of the string without using newlines, maybe that makes a difference.

Here is the exact PHP code that resulted in this problem:

function toSafeJavascript($unsafe) {
    $safe = str_replace(array("\r\n", "\n", "\r"), '', $unsafe); // Remove newlines
    $safe = preg_replace('/([\\/"])/', '\\\\$1', $safe);

    return $safe;
}
J0R1AN
  • 123
  • 3

1 Answers1

3

Neat puzzle! Yes, that filter is exploitable.

Attack

For historical reasons, ECMAScript (aka Javascript) supports an extended syntax when run in a web browser context. One syntax feature is the parsing of an HTML comment (<!-- foo -->) like a regular JS comment (/* foo */) as long as it does not span over multiple lines. (See B.1.1 HTML-like Comments of the spec.)

So, given the injection context...

<script>
x = "<?php echo toSafeJavascript($unsafe); ?>"
</script>

...and your filter function (which backslash-escapes " and / and removes newlines), the following attack vector triggers XSS:

\";prompt('gimme bounty')<!--

Here, the filter generates this valid JS code:

x = "\\";prompt('gimme bounty')<!--"

|-------||--------------------||----|
 string         payload        comment
 assignment

Defense

Never implement a filter for a common use case yourself. Even if you're certain the implementation is correct, anyone auditing the code will have to spend some extra time following the regex and replacement logic.

Also don't print user data within inline <script> code. The nested contexts make proper escaping more complicated. As I proposed in this answer, consider storing dynamic data in an HTML data-* attribute and retrieve it from static JS later.

If you still want to stick to the PHP-within-JS approach, consider a built-in function like json_encode() (with the proper flags!) as suggested here.

Arminius
  • 43,922
  • 13
  • 140
  • 136
  • Very cool solution! I totally forgot about still being able to use HTML tags. Thanks for the explanation – J0R1AN Sep 23 '21 at 14:42
  • @J0R1AN Thanks, just to be clear: It's not actually an HTML comment. You couldn't use arbitrary HTML tags within the script block. Semantically, it's a simple JS comment. It's an explicit JS syntax feature rather than a consequence of using an HTML comment. – Arminius Sep 23 '21 at 15:15
  • 1
    This solution is awesome. – Benoit Esnard Sep 23 '21 at 16:01