Web Notifications: PBackground Sender Principal Not Verified + SystemPrincipal Forgery
Categories
(Core :: DOM: Notifications, defect)
Tracking
()
People
(Reporter: t.satoki111, Assigned: tschuster)
References
(Blocks 1 open bug)
Details
(4 keywords, Whiteboard: [client-bounty-form][adv-main154-][adv-esr153.1-])
Attachments
(1 file, 2 external files)
|
4.60 KB,
application/x-javascript
|
Details |
Summary
PBackground::RecvCreateNotificationParent (BackgroundParentImpl.cpp:485) accepts child-supplied aPrincipal without binding it to the sending process's actual principal. No ValidatePrincipal() call exists.
BackgroundUtils.cpp's PrincipalInfoToPrincipal() converts TSystemPrincipalInfo to SystemPrincipal::Get() unconditionally — no process-type check.
NotificationUtils.cpp:56 IsNotificationAllowedFor() returns true unconditionally for SystemPrincipal, bypassing the permission manager entirely.
Result: a compromised content process sending TSystemPrincipalInfo completely bypasses all notification permission checks.
Attack Chain
Compromised content process (evil.com)
│
▼ [1] PBackground IPC
SendCreateNotificationParent(TSystemPrincipalInfo, ...)
│
▼ [2] BackgroundUtils.cpp:46-51 PrincipalInfoToPrincipal()
case TSystemPrincipalInfo:
principal = SystemPrincipal::Get(); // No process-type check
return principal;
│
▼ [3] BackgroundParentImpl.cpp:485 RecvCreateNotificationParent()
NotificationParent::CreateOnMainThread(aPrincipal, ...)
// No ValidatePrincipal(). Only action-count check.
│
▼ [4] NotificationParent.cpp RecvShow()
GetNotificationPermission(mArgs.mPrincipal, ...) // Runs on forged principal
│
▼ [5] NotificationUtils.cpp:56-58 IsNotificationAllowedFor()
if (aPrincipal->IsSystemPrincipal()) {
return true; // Permission manager never consulted
}
│
▼ Notification displayed (all permission checks bypassed)
Vulnerable Code
1. RecvCreateNotificationParent — No Sender Binding
ipc/glue/BackgroundParentImpl.cpp:485:
mozilla::ipc::IPCResult BackgroundParentImpl::RecvCreateNotificationParent(
Endpoint<dom::notification::PNotificationParent>&& aParentEndpoint,
NotNull<nsIPrincipal*> aPrincipal, // child-supplied, no validation
NotNull<nsIPrincipal*> aEffectiveStoragePrincipal, // child-supplied, no validation
const bool& aIsSecureContext,
const nsAString& aScope,
const IPCNotification& aNotification,
CreateNotificationParentResolver&& aResolver) {
dom::notification::NotificationParentArgs args{
aPrincipal, aEffectiveStoragePrincipal, aIsSecureContext,
nsString(aScope), aNotification};
nsresult rv = dom::notification::NotificationParent::CreateOnMainThread(
std::move(args), std::move(aParentEndpoint), std::move(aResolver));
// No ValidatePrincipal() call
return IPC_OK();
}
2. PrincipalInfoToPrincipal — Unconditional SystemPrincipal Conversion
ipc/glue/BackgroundUtils.cpp:46:
case PrincipalInfo::TSystemPrincipalInfo: {
principal = SystemPrincipal::Get(); // Converted even from content process
if (NS_WARN_IF(!principal)) {
return Err(NS_ERROR_NOT_INITIALIZED);
}
return principal; // No process-type rejection path
}
3. IsNotificationAllowedFor — SystemPrincipal Short-Circuit
dom/notification/NotificationUtils.cpp:56:
bool IsNotificationAllowedFor(nsIPrincipal* aPrincipal) {
if (aPrincipal->IsSystemPrincipal()) {
return true; // Permission manager never consulted
}
return aPrincipal->SchemeIs("file");
}
4. IPDL Protocol
dom/notification/PNotification.ipdl:
[ChildProc=anydom]
async protocol PNotification {
parent:
async Show(IPCImage? aIcon) returns (CopyableErrorResult rv);
async Close();
child:
async NotifyClick();
};
[ChildProc=anydom] is a process-type filter, not a principal-type restriction. It does not prevent a content process from claiming SystemPrincipal.
Contrast with PContent Handlers
PContent-routed notification IPC handlers call ValidatePrincipal() to verify the sender's actual principal. The PBackground RecvCreateNotificationParent() path has no equivalent check.
Exploitation Scenarios
Scenario 1: SystemPrincipal Forgery → Full Permission Bypass
- Attacker compromises a content process (JS engine exploit)
- Sends
SendCreateNotificationParent()withTSystemPrincipalInfo - Parent deserializes to
SystemPrincipal::Get() IsNotificationAllowedFor()returnstrueunconditionally- Notification displayed even if the user has never granted any notification permission
Scenario 2: Victim Origin Forgery → Phishing Notification
- Victim origin (e.g.,
bank.example.com) has notification permission granted - Attacker forges
ContentPrincipalInfo{origin: "https://bank.example.com"} - Parent checks permission against victim's record → allowed
- User sees a notification appearing to come from
bank.example.com - Notification click redirects to attacker's phishing site
Scenario 3: ServiceWorker Event Injection
- Victim origin has a ServiceWorker with a
notificationclickhandler - Attacker forges principal + scope to create a persistent notification
- User clicks the notification
NotificationHandler.cpproutesnotificationclickto the victim's ServiceWorker- Victim SW's click handler executes with attacker-crafted notification data
Suggested Fix
// Add at the top of RecvCreateNotificationParent():
if (aPrincipal->IsSystemPrincipal()) {
return IPC_FAIL(this, "Content process cannot claim SystemPrincipal");
}
// Additionally: bind aPrincipal to the sender ContentParent's actual principal
Structural fix: reject TSystemPrincipalInfo from content processes in PrincipalInfoToPrincipal() or add sender binding at the RecvCreateNotificationParent() level.
Updated•8 months ago
|
Comment 1•8 months ago
|
||
Protecting notifications (and indeed, many other permissions) against a compromised child process is not currently part of our threat mode.
Updated•8 months ago
|
Updated•7 months ago
|
Comment 3•6 months ago
|
||
The severity field for this bug is set to S3. However, the following bug duplicate has higher severity:
- Bug 2031811: S2
:edenchuang, could you consider increasing the severity of this bug to S2?
For more information, please visit BugBot documentation.
Updated•6 months ago
|
| Assignee | ||
Updated•4 months ago
|
| Assignee | ||
Comment 6•4 months ago
|
||
Comment 8•4 months ago
|
||
Updated•4 months ago
|
Updated•4 months ago
|
Updated•3 months ago
|
Updated•3 months ago
|
Comment 10•3 months ago
|
||
firefox-esr153 Uplift Approval Request
- User impact if declined/Reason for urgency: General hardening of principals in IPC
- Code covered by automated testing?: yes
- 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: Simple patch and we haven't seen any crashes from this apparently.
- String changes made/needed?: no
- Is Android affected?: yes
| Assignee | ||
Comment 11•3 months ago
|
||
Original Revision: https://phabricator.services.mozilla.com/D309842
| Assignee | ||
Updated•3 months ago
|
Updated•3 months ago
|
Updated•3 months ago
|
Updated•3 months ago
|
Comment 12•3 months ago
|
||
| uplift | ||
Updated•3 months ago
|
Updated•3 months ago
|
Updated•19 days ago
|
Description
•