ContentParent::RecvAddCertException accepts arbitrary host/cert override requests from content, enabling permanent certificate-validity overrides for any host after content-process compromise
Categories
(Core :: DOM: Content Processes, defect)
Tracking
()
People
(Reporter: cs.attila, Unassigned)
References
()
Details
(Keywords: ai-involved, reporter-external, Whiteboard: [client-bounty-form])
Attachments
(1 file)
|
1.85 KB,
text/plain
|
Details |
Summary
ContentParent::RecvAddCertException appears to accept a content-supplied hostname, port, origin attributes, certificate, and temporary/permanent flag and forwards them to nsCertOverrideService::RememberValidityOverride(...) without verifying that the sending content process is allowed to add a certificate-validity override for that hostname.
A compromised content process can therefore attempt to install a certificate-validity override for an arbitrary host, with an attacker-supplied certificate, without going through the visible about:certerror / “Accept the Risk and Continue” user-confirmation flow for that host.
This is not a normal web-content JavaScript-only attack and is not a sandbox escape. The precondition is a compromised content process / renderer-RCE-capable primitive that can send raw PContent IPC. Exploitation of the installed override for traffic compromise also requires a network MITM position for the targeted host.
Affected path
Primary path:
dom/ipc/PContent.ipdlasync AddCertException(nullable nsIX509Cert aCert, nsCString aHostName, int32_t aPort, OriginAttributes aOriginAttributes, bool aIsTemporary)
dom/ipc/ContentParent.cppContentParent::RecvAddCertException(...)
security/manager/ssl/nsCertOverrideService.cppnsCertOverrideService::RememberValidityOverride(...)nsCertOverrideService::AddEntryToList(...)
Legitimate UI path:
dom/base/Document.cppDocument::AddCertException(...)
toolkit/content/aboutNetError.mjs/net-error-card.mjs- visible user action on the certificate-error page
Missing validation
The intended invariant appears to be:
A request to install a certificate-validity override for a host must be bound to a deliberate user action on the certificate-error UI for that exact host, and to the content process / browsing context that is displaying that host’s certificate error.
The observed parent-side path appears to trust the content-supplied aHostName directly:
ContentParent::RecvAddCertException(..., nsCString aHostName, int32_t aPort,
OriginAttributes aOriginAttributes,
nsIX509Cert* aCert, bool aIsTemporary, ...)
{
nsCOMPtr<nsICertOverrideService> overrideService =
do_GetService(NS_CERTOVERRIDE_CONTRACTID);
...
nsresult rv = overrideService->RememberValidityOverride(
aHostName, aPort, aOriginAttributes, aCert, aIsTemporary);
...
}
RememberValidityOverride(...) appears to validate argument shape — non-empty ASCII hostname, valid port, non-null certificate, main-thread execution — then stores an override keyed by the supplied host/port/origin attributes and certificate fingerprint. I do not see a check that ties aHostName to the sending content process, the browsing context that encountered a certificate error, or a parent-authoritative failed channel/load.
Attack sketch
Precondition:
The attacker has compromised a content process and can send raw PContent IPC. To turn the installed override into traffic compromise, the attacker must also be able to MITM the user’s connection to the targeted host.
Steps:
-
The attacker creates or obtains an
nsIX509Certfor an attacker-controlled certificate. -
From the compromised content process, the attacker sends a raw
AddCertExceptionIPC similar to:
SendAddCertException(attackerCert,
"victim.example"_ns,
443,
OriginAttributes{},
/* aIsTemporary = */ false)
-
The parent calls
nsCertOverrideService::RememberValidityOverride(...)and installs a certificate-validity override for(victim.example, 443, originAttributes, sha256(attackerCert)). -
Later, when Firefox connects to
https://victim.example, an attacker with a network MITM position presentsattackerCert. -
The certificate validation failure that would normally be fatal now matches the stored override, so Firefox accepts the connection. The attacker can then decrypt or modify the targeted host’s TLS traffic.
The attacker can repeat this for multiple hostnames. The key issue is that the override host is content-supplied and not proven to be the host for which the user clicked the certificate-error UI.
Claimed impact
A compromised content process can pre-install a certificate-validity override for an arbitrary host without user confirmation for that host.
With an additional network MITM position, this can disable TLS certificate validation for the targeted host and enable full MITM of that host’s traffic using the attacker-supplied certificate.
Important limitations:
- This is not a normal JavaScript-only web exploit.
- This requires a compromised content process or equivalent raw-IPC primitive.
- This is not a sandbox escape.
- Actual TLS traffic compromise additionally requires a network MITM position.
- I have not attached a standalone renderer-RCE PoC; the proof is source-level analysis of the parent-side IPC validation gap.
Why this looks like a missing validation
The legitimate flow is a host-specific certificate-error UI decision. The parent-side IPC path appears to accept the host directly from the child process and store the override without verifying that the request is tied to a certificate-error page / failed load for that host.
That makes the request identity (aHostName) attacker-controlled under a compromised-content-process model.
Suggested fix
Suggested shape:
-
Do not allow arbitrary content processes to install certificate overrides for arbitrary hostnames.
-
Bind the override request to a parent-authoritative failed TLS channel/load or a
BrowsingContext/WindowContextthat is known to be displaying the certificate-error page for that exact hostname. -
In
ContentParent::RecvAddCertException(...), verify that:- the sending content process owns the relevant browsing context;
- the failed-load hostname matches
aHostName; - the user action occurred in the certificate-error UI for that load;
- the supplied certificate matches the failed certificate for that load.
-
Consider rejecting
aIsTemporary=falseunless the request is provably tied to the visible “Accept the Risk” UI and the exact failed host. -
Alternatively, move override installation entirely off this generic content-process IPC path and perform it through a parent-process actor bound to the failed channel/load.
Reporter notes
I am reporting this privately as a security-sensitive bug.
I am not claiming exploitation in the wild.
I am not claiming a standalone sandbox escape.
I can help develop a targeted Mozilla test / IPC-level reproducer if the team can confirm the preferred harness style for exercising PContent::AddCertException from a compromised-content-process model.
| Reporter | ||
Comment 1•2 months ago
|
||
Updated•2 months ago
|
Updated•2 months ago
|
Description
•