Closed Bug 2034489 (CVE-2026-12302) Opened 6 months ago Closed 5 months ago

Creating a `SharedWorker` using a constructor that was saved prior to a same-origin navigation might bypass CSP

Categories

(Core :: DOM: Security, defect)

defect

Tracking

()

RESOLVED FIXED
153 Branch
Tracking Status
firefox-esr115 152+ fixed
firefox-esr140 152+ fixed
firefox150 --- wontfix
firefox151 --- wontfix
firefox152 + fixed
firefox153 + fixed

People

(Reporter: lebr0nli, Assigned: tschuster)

References

Details

(Keywords: csectype-mitigation-bypass, reporter-external, sec-moderate, Whiteboard: [client-bounty-form][adv-main152+][adv-esr115.37+][adv-esr140.12+])

Attachments

(1 file, 4 external files)

Attached file index.html —

VULNERABILITY DETAILS

CSP is not properly enforced when creating a SharedWorker using a constructor that was saved prior to a same-origin navigation and invoked after the navigation completes. This allows an attacker to create a SharedWorker without a worker-src directive allowance and execute code in the context of the SharedWorker that would otherwise be blocked by the connect-src and script-src CSP directives.

VERSION

Firefox Version: 150.0

The vulnerability is also present in Firefox 152.0a1 (2026-04-23) nightly

REPRODUCTION CASE

  1. Run python3 -m http.server 1337 to serve the attached index.html.
  2. Navigate to http://127.0.0.1:1337/.
  3. The shared worker, created with a blob: URL, will attempt to establish connections via fetch, EventSource, and WebSocket to http://127.0.0.1:1337/connect-src-bypass and execute importScripts('http://127.0.0.1:1337/script-src-bypass?eval='+eval('21*2===42')).

Expected Behavior: worker-src, connect-src, and script-src should block the above operations. Or, new SharedWorker should probably throw an error just as Worker and navigator.serviceWorker.register do.

Actual Behavior: CSP is bypassed.

CREDIT INFORMATION

Reporter credit: lebr0nli of National Yang Ming Chiao Tung University, Dept. of CS, Security and Systems Lab.

Flags: sec-bounty?
Group: firefox-core-security → dom-core-security
Product: Firefox → Core
Component: Security → DOM: Security

Note that in regards to the currently attached edge case, there are potential edge-cases related to the navigation of an initial about:blank to a same-origin URL per explicit spec carveout, although I think the explicit reload may mitigate.

More of an implementation note is that I believe our worker implementations still diverge from spec and other browsers in preferring the CSP served with the worker over the CSP of the owner, but that's moot for blob URLs where the CSP would of course explicitly be inherited from the creator of the blob URL.

But separate from that, I think our behavior is potentially correct per spec. The SharedWorker constructor explicitly uses the relevant settings object of the constructor itself, not the caller. The interesting situation here is that the destruction of the document should remove it from the owner set of the SharedWorker and there's sort of a spec race there, although the lifetime of SharedWorkers has always been a bit hand-wavey. Certainly, since SharedWorkers are explicitly intended to be able to survive navigation (we now have a formalized between-loads shared worker timeout), it does make sense that the SharedWorker could continue to exist for a bit as a permissible worker. (Although I should say it's quite believable we may have bugs in this very edge-casey part of the spec and we may otherwise diverge from other browsers and it of course would be good to try and provide consistency.)

(In reply to lebr0nli (Alan Li) [:lebr0nli] from comment #0)

Expected Behavior: worker-src, connect-src, and script-src should block the above operations. Or, new SharedWorker should probably throw an error just as Worker and navigator.serviceWorker.register do.

Can you provide clarity on what in the spec would dictate this expected behavior?

Flags: needinfo?(godsons-goner.3j)

Hi,

Thanks for reviewing my report!

The SharedWorker constructor explicitly uses the relevant settings object of the constructor itself, not the caller.

I fully agree with this point. However, based on 7.8. CSP Inheriting to avoid bypasses, the iframe's about:blank document should inherit the CSP of the context that created it.

Therefore, I believe the relevant settings object of the SharedWorker constructor here should use the same CSP as index.html (which happens to be the same as its caller in this case). This means the inherited worker-src 'none' directive should block the SharedWorker created by the blob: URL. Even in cases where worker-src allows blob: URLs, script-src and connect-src should also block those operations executed inside the SharedWorker.

In addition, to avoid any confusion regarding my statement in title and "VULNERABILITY DETAILS", the "same-origin navigation" I mentioned refers to navigating to a page that has the same or a stricter CSP, not navigating to a same-origin page that has no CSP. I used about:blank in the PoC because it works even with frame-src 'none' and is easier to reproduce. Sorry for any confusion this may have caused.

Or, new SharedWorker should probably throw an error just as Worker and navigator.serviceWorker.register do.

Regarding my statement on this part, I couldn't find a specific spec. However, I observed this behavior in the current implementations of other kinds of workers in Firefox, which throw an error when the corresponding document is about to be discarded. (This also seems true for all kinds of workers in Chromium and WebKit.)

By the way, I think this bug is quite similar to another bug I reported.
Perhaps that bug report can provide additional context to support my perspective.

Looking forward to your thoughts!

Flags: needinfo?(godsons-goner.3j)

Thanks for the clarification. I'd mis-parsed the vulnerability details description and not sufficiently reconciled that against the test case. Thanks for the quick reply!

If this isn't a case of us letting the SharedWorker decide whatever CSP it wants for itself because of how we handle the top-level load, then it sounds like what's happening here probably relates to how we try and pull the client and thereby the CSP off of the not-fully-active global. I won't be able to dig back into this immediately but set a needinfo to keep it on my shorter list, but Tom (or others), there's no need to wait for me if you want to dig in more.

Flags: needinfo?(bugmail)
Flags: needinfo?(bugmail)

(mis-clicked some things)

Flags: needinfo?(bugmail)

We already disallow creating Workers in windows that are going away, so we probably just want to do this:

diff --git a/dom/workers/sharedworkers/SharedWorker.cpp b/dom/workers/sharedworkers/SharedWorker.cpp
index 7593414e2adf..d2b025d28d76 100644
--- a/dom/workers/sharedworkers/SharedWorker.cpp
+++ b/dom/workers/sharedworkers/SharedWorker.cpp
@@ -82,6 +82,11 @@ already_AddRefed<SharedWorker> SharedWorker::Constructor(
       do_QueryInterface(aGlobal.GetAsSupports());
   MOZ_ASSERT(window);
 
+  if (!window->IsCurrentInnerWindow()) {
+    aRv.ThrowInvalidStateError("Cannot create worker for a going to be discarded document");
+    return nullptr;
+  }
+
   // Our current idiom is that storage-related APIs specialize for the system
   // principal themselves, which is consistent with StorageAllowedForwindow not
   // specializing for the system principal.  Without this specialization we

The deeper issue is basically a repeat of bug 1966927. Instead of requiring a ClientInfo to exist in ChannelFromScriptURL, we just silently fallback to not using one at all. We should just abort in that case.

Okay, I guess I will take this.

Assignee: nobody → tschuster
Status: UNCONFIRMED → ASSIGNED
Ever confirmed: true
Attached file (secure) —

(Bug 1966927 is where we introduced the check for dedicated workers.)

Flags: needinfo?(bugmail)
See Also: → CVE-2025-6427

Comment on attachment 9585488 [details]
(secure)

Security Approval Request

  • How easily could an exploit be constructed based on the patch?: I think it's probably feasible by looking for other bugs that involve discarded documents.
  • Do comments in the patch, the check-in comment, or tests included in the patch paint a bulls-eye on the security problem?: Unknown
  • Which branches (beta, release, and/or ESR) are affected by this flaw, and do the release status flags reflect this affected/unaffected state correctly?: all
  • If not all supported branches, which bug introduced the flaw?: None
  • Do you have backports for the affected branches?: Yes
  • If not, how different, hard to create, and risky will they be?:
  • How likely is this patch to cause regressions; how much testing does it need?: Considering that we already have the same behavior for Worker constructor hopefully low.
  • Is the patch ready to land after security approval is given?: Yes
  • Is Android affected?: Yes
Attachment #9585488 - Flags: sec-approval?

Unfortunately the sec-approval flag again somehow got lost.

Attachment #9585488 - Flags: sec-approval? → sec-approval+

https://hg-edge.mozilla.org/mozilla-central/rev/31fb1e8da1f7

Please add Beta and ESR140 uplift requests on this when you get a chance.

Group: dom-core-security → core-security-release
Status: ASSIGNED → RESOLVED
Closed: 5 months ago
Resolution: --- → FIXED
Target Milestone: --- → 153 Branch

firefox-beta Uplift Approval Request

  • User impact if declined/Reason for urgency: Content-Security-Policy bypass on websites.
  • 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: This aligns our behavior with Workers. There is a low chance this might break some website depending on this, but this a huge edge case and IIRC other browsers behave similarly.
  • String changes made/needed?: no
  • Is Android affected?: yes
Attachment #9588330 - Flags: approval-mozilla-beta?
Attached file (secure) —

firefox-esr140 Uplift Approval Request

  • User impact if declined/Reason for urgency: Content-Security-Policy bypass on websites.
  • 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: This aligns our behavior with Workers. There is a low chance this might break some website depending on this, but this a huge edge case and IIRC other browsers behave similarly.
  • String changes made/needed?: no
  • Is Android affected?: yes
Attachment #9588331 - Flags: approval-mozilla-esr140?
Attached file (secure) —
Attachment #9588330 - Flags: approval-mozilla-beta? → approval-mozilla-beta+

[Tracking Requested - why for this release]: This is something I want to ensure we address in the spec and it would be nice to make sure it's no longer an issue on all supported branches before discussing it. I'll issue the uplift request as well now.

Attachment #9588510 - Flags: approval-mozilla-esr115?

firefox-esr115 Uplift Approval Request

  • User impact if declined/Reason for urgency: Content-Security-Policy bypass on websites.
  • 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: "This aligns our behavior with Workers. There is a low chance this might break some website depending on this, but this a huge edge case and IIRC other browsers behave similarly."
  • String changes made/needed?: no
  • Is Android affected?: yes
Attachment #9588331 - Flags: approval-mozilla-esr140? → approval-mozilla-esr140+
Attachment #9588510 - Flags: approval-mozilla-esr115? → approval-mozilla-esr115+
QA Whiteboard: [sec] [uplift] [qa-triage-done-c153/b152]

Even in cases where worker-src allows blob: URLs, script-src and connect-src should also block those operations executed inside the SharedWorker.

Flags: sec-bounty? → sec-bounty+

Hi,

Thanks for the bounty and the quick fix, really appreciated!

Just in case it affects the evaluation or the security advisory: I want to flag that the quote in #c21 was a bit incomplete, with only this, it might look like it need worker-src blob: to be exploitable. The full quote from my original comment in #c2 should be:

This means the inherited worker-src 'none' directive should block the SharedWorker created by the blob: URL. Even in cases where worker-src allows blob: URLs, script-src and connect-src should also block those operations executed inside the SharedWorker.

This bug does not require blob: in worker-src to be exploited. This bug also bypasses the worker-src directive when creating SharedWorker, new OldSharedWorker('blob:...') works with worker-src 'none' before the patch. (You can check with the index.html I attached with the report.)

Whiteboard: [client-bounty-form] → [client-bounty-form][adv-main152+]
Whiteboard: [client-bounty-form][adv-main152+] → [client-bounty-form][adv-main152+][adv-esr115.37+]
Whiteboard: [client-bounty-form][adv-main152+][adv-esr115.37+] → [client-bounty-form][adv-main152+][adv-esr115.37+][adv-esr140.12+]
Alias: CVE-2026-12302
Group: core-security-release
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: