PCookieStore::GetRequestOnMainThread missing CheckContentProcessSecurity - cross-origin cookie read
Categories
(Core :: Networking: Cookies, defect, P2)
Tracking
()
| 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)
|
22.04 KB,
text/x-python
|
Details |
=== 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:
-
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.
-
Content-controlled OriginAttributes: partitionKey and privateBrowsingId come from the child. A compromised process reads cookies from any partition or from private browsing sessions.
-
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
Updated•8 months ago
|
Comment 1•8 months ago
|
||
Seems reasonable to add CheckContentProcessSecurity for GetRequestOnMainThread.
Updated•8 months ago
|
Comment 2•8 months ago
|
||
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 | ||
Updated•7 months ago
|
| Assignee | ||
Comment 3•7 months ago
|
||
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.
| Assignee | ||
Comment 4•7 months ago
|
||
| Reporter | ||
Comment 5•7 months ago
|
||
Thanks, Randell. I should have caught these inaccuracies and your comments appear correct. Thanks for the thorough review. - Richard
Comment 7•7 months ago
|
||
Updated•7 months ago
|
Comment 8•7 months ago
|
||
The patch landed in nightly and beta is affected.
:jesup, is this bug important enough to require an uplift?
- If yes, please nominate the patch for beta approval.
- See https://wiki.mozilla.org/Release_Management/Requesting_an_Uplift for documentation on how to request an uplift.
- If no, please set
status-firefox150towontfix.
For more information, please visit BugBot documentation.
Updated•7 months ago
|
Comment 9•6 months ago
|
||
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
| Assignee | ||
Comment 10•6 months ago
|
||
Original Revision: https://phabricator.services.mozilla.com/D290490
Updated•6 months ago
|
Updated•6 months ago
|
Comment 11•6 months ago
|
||
| uplift | ||
Updated•6 months ago
|
Updated•6 months ago
|
Updated•6 months ago
|
Updated•5 months ago
|
Updated•2 months ago
|
Comment 13•19 days ago
|
||
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.
Description
•