Closed Bug 2059511 Opened 3 months ago Closed 3 months ago

Missing principal validation in RecvPServiceWorkerRegistrationConstructor enables cross-origin notification theft

Categories

(Core :: DOM: Service Workers, defect)

x86_64
Linux
defect

Tracking

()

RESOLVED DUPLICATE of bug 2054667

People

(Reporter: med.mbarek.isi, Unassigned)

Details

(Keywords: reporter-external)

Attachments

(5 files)

Summary

Missing principal validation in RecvPServiceWorkerRegistrationConstructor allows a compromised content process to forge a PServiceWorkerRegistration actor targeting another origin, enabling cross-origin notification data theft and service worker lifecycle manipulation.

Root Cause

BackgroundParentImpl::RecvPServiceWorkerRegistrationConstructor (ipc/glue/BackgroundParentImpl.cpp, line 1343) accepts both IPCServiceWorkerRegistrationDescriptor and IPCClientInfo from the content process without validating that the claimed principal matches the process's remote type.

Bug 2045721 (landed 2026-07-21) added ClientIsValidPrincipalInfo checks to other PBackground handlers (e.g., RecvCreateWebTransportParent at line 501) but missed RecvPServiceWorkerRegistrationConstructor.

Vulnerable code

// ipc/glue/BackgroundParentImpl.cpp:1342-1356
mozilla::ipc::IPCResult
BackgroundParentImpl::RecvPServiceWorkerRegistrationConstructor(
    PServiceWorkerRegistrationParent* aActor,
    const IPCServiceWorkerRegistrationDescriptor& aDescriptor,
    const IPCClientInfo& aForClient) {
  // NO ClientIsValidPrincipalInfo check — contrast with line 501
  dom::InitServiceWorkerRegistrationParent(aActor, aDescriptor, aForClient);
  return IPC_OK();
}

Compare with the protected handler (same file, line 494):

// RecvCreateWebTransportParent — HAS the check (added by Bug 2045721)
if (!dom::ClientIsValidPrincipalInfo(aClientInfo.principalInfo(),
                                     BackgroundParent::GetRemoteType(this))) {
  return IPC_FAIL(this, "...");
}

Impact

A compromised content process (e.g., webIsolated=http://attacker.com) can send a forged PServiceWorkerRegistration constructor message claiming to be any other origin (e.g., http://victim.com). The parent process binds the actor to the victim's real service worker registration, granting the attacker:

  1. Cross-origin notification theft: getNotifications() reads the victim's stored notifications (title, body, tag, icon, structured data) via ServiceWorkerRegistrationProxy::GetNotifications → GetStoredNotificationsForScope(forged_principal, ...). No authorization check exists in the notification storage path.

  2. Service worker unregistration: Unregister() calls swm->Unregister(mReg->Principal(), ...) using the victim's real registration, allowing the attacker to remove the victim's service worker.

  3. Forced service worker update: Update() triggers an update check on the victim's registration.

  4. NavigationPreload manipulation: SetNavigationPreloadEnabled/Header() modifies the victim's navigation preload settings.

All of these use mReg (the victim's real ServiceWorkerRegistrationInfo) or mListeningClientInfo (constructed from the forged IPCClientInfo), which are set during ServiceWorkerRegistrationProxy::Init with no validation.

Affected Versions

  • Firefox Release 153.0.1
  • Firefox Beta 154.0b4
  • Firefox ESR 153.0esr
  • Firefox Nightly 155.0a1

Bug 2045721 landed after the release branch cut (merge date: 2026-07-20) and only fixed other handlers.

Steps to Reproduce

This is an IPC-level vulnerability. The PoC patches Firefox source code to simulate what a compromised renderer would do (sending a forged IPC message). A real attacker with renderer compromise would manipulate the IPC channel directly.

Setup

  1. Apply the attached patch (poc_patch.diff) to mozilla-central
  2. Build with --enable-debug --disable-optimize
  3. Start two HTTP servers:
    • python3 -m http.server 8081 serving victim/ directory (index.html + sw.js)
    • python3 -m http.server 8080 serving attacker/ directory (poc.html + sw.js)
  4. Create a profile with prefs: permissions.default.desktop-notification=1, dom.serviceWorkers.enabled=true, dom.serviceWorkers.testing.enabled=true, devtools.console.stdout.content=true
  5. Launch Firefox with both tabs: firefox http://localhost:8081/ http://localhost:8080/poc.html
  6. Wait ~30 seconds for the debug build to complete setup

What the source patch does

  • ServiceWorkerRegistration.cpp: When origin is localhost:8080, rewrites the outgoing PServiceWorkerRegistration constructor IPC to claim localhost:8081's principal, scope, and registration ID 1. This simulates a compromised renderer forging the IPC message.
  • ServiceWorkerRegistration.cpp: Bypasses a content-side MOZ_DIAGNOSTIC_ASSERT(MatchesDescriptor(...)) that is irrelevant when the renderer is compromised.
  • BackgroundParentImpl.cpp: Adds stderr logging to show the parent accepts the forged message without validation.
  • ServiceWorkerRegistrationProxy.cpp: Adds stderr logging to show the forged principal is used to query notification storage.

Expected result

The parent process should reject the forged IPC because the claimed principal doesn't match the content process's remote type.

Actual result

The parent accepts the forged IPC. The attacker's page reads the victim's stored notification:

[PoC-JS] Notifications stolen: 1
[PoC-JS] STOLEN title="Confidential: Account Alert" body="Your balance is $50,000. Transfer code: XYZZY-SECRET-42" tag="account-alert"
[PoC-JS] STOLEN data={"account":"1234-5678-9012","pin":"9999","ssn":"123-45-6789"}
[PoC-JS] === BUG CONFIRMED === Cross-origin notification theft successful

Parent-side stderr confirms the missing check:

[PoC] FORGED PServiceWorkerRegistration: http://localhost:8080 -> http://localhost:8081 (id=1)
[PoC] RecvPServiceWorkerRegistrationConstructor: remoteType=webIsolated=http://localhost scope=http://localhost:8081/ id=1 — NO ClientIsValidPrincipalInfo check!
[PoC] GetNotifications: reading notifications for origin=http://localhost:8081 scope=http://localhost:8081/

Suggested Fix

Add ClientIsValidPrincipalInfo and ValidatePrincipalInfo checks to RecvPServiceWorkerRegistrationConstructor, matching the pattern from Bug 2045721:

mozilla::ipc::IPCResult
BackgroundParentImpl::RecvPServiceWorkerRegistrationConstructor(
    PServiceWorkerRegistrationParent* aActor,
    const IPCServiceWorkerRegistrationDescriptor& aDescriptor,
    const IPCClientInfo& aForClient) {
  if (!dom::ClientIsValidPrincipalInfo(aForClient.principalInfo(),
                                       BackgroundParent::GetRemoteType(this))) {
    return IPC_FAIL(this,
        "PServiceWorkerRegistration ClientInfo principal not valid for remote type");
  }
  if (!BackgroundParent::ValidatePrincipalInfo(this, aDescriptor.principalInfo())) {
    return IPC_FAIL(this,
        "PServiceWorkerRegistration descriptor principal is invalid");
  }
  dom::InitServiceWorkerRegistrationParent(aActor, aDescriptor, aForClient);
  return IPC_OK();
}

Thank you for the report. This issue has been determined to be a duplicate of bug 2054667.

This bug is the same underlying vulnerability as the canonical bug 2054667:

  • Same handler: BackgroundParentImpl::RecvPServiceWorkerRegistrationConstructor
  • Same root cause: no validation of the child-supplied IPCServiceWorkerRegistrationDescriptor / IPCClientInfo principals against the sending process
  • Same impact: cross-origin notification theft via GetNotifications, plus Unregister/Update/navigation-preload tampering
Group: core-security → dom-core-security
Status: UNCONFIRMED → RESOLVED
Closed: 3 months ago
Duplicate of bug: 2054667
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: