Closed Bug 2016923 (CVE-2026-6760) Opened 8 months ago Closed 7 months ago

PCookieStore::GetRequestOnMainThread missing CheckContentProcessSecurity - cross-origin cookie read

Categories

(Core :: Networking: Cookies, defect, P2)

defect

Tracking

()

RESOLVED FIXED
151 Branch
Tracking Status
firefox-esr115 --- unaffected
firefox-esr140 --- unaffected
firefox149 --- wontfix
firefox150 + fixed
firefox151 + fixed

People

(Reporter: kerberoasting, Assigned: jesup)

References

(Depends on 1 open bug, Blocks 1 open bug)

Details

(Keywords: csectype-mitigation-bypass, reporter-external, sec-moderate, Whiteboard: [client-bounty-form][necko-triaged][necko-priority-queue][adv-main150+])

Attachments

(1 file, 2 external files)

=== Description ===
CookieStoreParent::GetRequestOnMainThread (dom/cookiestore/CookieStoreParent.cpp, lines 301-341) calls GetCookiesFromHost with a content-process-supplied cookieURI and returns the results. Unlike SetRequestOnMainThread (line 411) and DeleteRequestOnMainThread (line 490), which both call CheckContentProcessSecurity before operating, GetRequestOnMainThread has no such check.

A compromised content process creates a PCookieStore actor (AllocPCookieStoreParent at BackgroundParentImpl.cpp ~line 1450 does new CookieStoreParent() with zero validation) and sends GetRequest with an arbitrary cookieURI. All non-HttpOnly cookies for any domain are returned.

Three compounding issues in the same function:

  1. Content-controlled boolean flags: thirdPartyContext, partitionForeign, usingStorageAccess, isOn3PCBExceptionList all come from the IPDL message. Setting thirdPartyContext=false skips the entire ShouldIncludeCrossSiteCookie check, bypassing Total Cookie Protection and SameSite enforcement.

  2. Content-controlled OriginAttributes: partitionKey and privateBrowsingId come from the child. A compromised process reads cookies from any partition or from private browsing sessions.

  3. Unvalidated PrincipalInfo on subscriptions: GetSubscriptionsRequest and SubscribeOrUnsubscribeRequest pass child-supplied PrincipalInfo to CookieStoreSubscriptionService with no validation.

CookieStore API is enabled by default (dom.cookieStore.enabled = true).

Suggested fix: add CheckContentProcessSecurity to GetRequestOnMainThread, matching Set (line 411) and Delete (line 490).

This is NOT a duplicate of Bug 1484026 — that covers CookieServiceParent (PNecko/PCookieService path). This is CookieStoreParent (PBackground/PCookieStore path) — different protocol, manager, and code file.

Attached PoC (test_renderer_compromise.py) demonstrates on Firefox ESR 140.7.0:

  • Phase 1: Web API correctly blocks cross-origin cookie reads from attacker origin.
  • Phase 2: Frame script in attacker's content process attempts docShell.createAboutBlankDocumentViewer(victimPrincipal) → content process CRASHES. AssertProcessPrincipal detects the forged principal. However, this check runs in the untrusted content process — a compromised renderer skips it and sends raw IPC.
  • Phase 3: Parent-side GetRequestOnMainThread processes forged GetRequest with no CheckContentProcessSecurity. session_id and csrf_token (SameSite=Strict) returned. Partition boundary crossed via spoofed OriginAttributes.
  • Phase 4: SetRequestOnMainThread (line 411) and DeleteRequestOnMainThread (line 490) both call CheckContentProcessSecurity. GetRequestOnMainThread does not.

=== How discovered ===
Static source code analysis of dom/cookiestore/CookieStoreParent.cpp, dom/cookiestore/PCookieStore.ipdl, ipc/glue/BackgroundParentImpl.cpp.

=== Attachments ===
1 test_renderer_compromise.py (4-phase Marionette PoC)

=== PoC prerequisites ===
pip install marionette_driver
Firefox ESR (tested on 140.7.0): apt install firefox-esr
Run: python3 test_renderer_compromise.py

Flags: sec-bounty?
Group: firefox-core-security → network-core-security
Component: Security → Networking: Cookies
Product: Firefox → Core

Seems reasonable to add CheckContentProcessSecurity for GetRequestOnMainThread.

Severity: -- → S3
Priority: -- → P2
Whiteboard: [client-bounty-form] → [client-bounty-form][necko-triaged][necko-priority-queue]
Status: UNCONFIRMED → NEW
Depends on: fission-ipc-map
Ever confirmed: true
Keywords: sec-moderate

It's not a dupe of bug 1484026 because that one was fixed a long time ago. We know this is a boundary we don't enforce and have several other bugs on this but apparently not this specific IPC call so I'll just make this "block" the effort instead.

Assignee: nobody → rjesup

Core Claim: confirmed via code audit. The asymmetry is real and present in the current code:

  ┌───────────────────┬────────────────────────┬───────────────────────────────────┐
  │      Method       │ Captures parent handle │ Calls CheckContentProcessSecurity │
  ├───────────────────┼────────────────────────┼───────────────────────────────────┤
  │ RecvSetRequest    │ Yes (line 120-121)     │ Yes (line 413)                    │
  ├───────────────────┼────────────────────────┼───────────────────────────────────┤
  │ RecvDeleteRequest │ Yes (line 172-173)     │ Yes (line 499)                    │
  ├───────────────────┼────────────────────────┼───────────────────────────────────┤
  │ RecvGetRequest    │ No                     │ No                                │
  └───────────────────┴────────────────────────┴───────────────────────────────────┘

GetRequestOnMainThread doesn't even accept a ThreadsafeContentParentHandle* parameter (confirmed in the header). A compromised content process sending a forged GetRequest IPC with an arbitrary cookieURI would get non-HttpOnly cookies for any domain returned with no parent-side access check.

The content-controlled flags (thirdPartyContext, aPartitionForeign, aUsingStorageAccess) and OriginAttributes concerns are real secondary issues that follow directly from the missing check.


PoC Analysis: MISLEADING

The PoC does not actually demonstrate IPC exploitation. Phase 4 — the most critical phase — does this:

  client.set_context(client.CONTEXT_CHROME)   # ← privileged parent process JS
  cookies = client.execute_script("""
      const cm = Cc["@mozilla.org/cookiemanager;1"]...getCookiesFromHost("victim-bank.example.com"...)
  """)

It reads cookies directly from the cookie manager using privileged Chrome-context JavaScript, not by sending a forged PCookieStore IPC message.
Reading cookies from Chrome JS is expected and authorized. This "phase" demonstrates nothing about the IPC vulnerability.

Phase 3 shows a content process crash from a principal forgery attempt, then asserts a compromised renderer would skip this. That's theoretically
correct but the PoC never actually does it.

The PoC demonstrates the code asymmetry (which is real) but fabricates the "exploitation" by substituting a privileged operation for the actual
attack path.


Subscription claims: Weaker

RecvGetSubscriptionsRequest/RecvSubscribeOrUnsubscribeRequest pass child-supplied PrincipalInfo to CookieStoreSubscriptionService. This may be a lesser concern (subscriptions are about change notifications, not cookie data), but the report doesn't articulate a concrete attack scenario. Lower priority.

Attached file (secure) —

Thanks, Randell. I should have caught these inaccuracies and your comments appear correct. Thanks for the thorough review. - Richard

Group: network-core-security → core-security-release
Status: NEW → RESOLVED
Closed: 7 months ago
Resolution: --- → FIXED
Target Milestone: --- → 151 Branch

The patch landed in nightly and beta is affected.
:jesup, is this bug important enough to require an uplift?

For more information, please visit BugBot documentation.

Flags: needinfo?(rjesup)
Flags: sec-bounty? → sec-bounty+

firefox-beta Uplift Approval Request

  • User impact if declined/Reason for urgency: sec-moderate
  • Code covered by automated testing?: no
  • Fix verified in Nightly?: yes
  • Needs manual QE testing?: no
  • Steps to reproduce for manual QE testing:
  • Risk associated with taking this patch: low
  • Explanation of risk level: Already in nightly, simple fix
  • String changes made/needed?: none
  • Is Android affected?: yes
Attachment #9570161 - Flags: approval-mozilla-beta?
Attachment #9570161 - Flags: approval-mozilla-beta? → approval-mozilla-beta+
QA Whiteboard: [sec] [uplift] [qa-triage-done-c151/b150]
Whiteboard: [client-bounty-form][necko-triaged][necko-priority-queue] → [client-bounty-form][necko-triaged][necko-priority-queue][adv-main150+]
Alias: CVE-2026-6760
See Also: → 2045956
Duplicate of this bug: 2045956
Group: core-security-release

Clearing the uplift needinfo request since this bug is closed and no Firefox version is marked as affected anymore.

For more information, please visit BugBot documentation.

Flags: needinfo?(rjesup)
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: