PBrowser::NewWindowGlobal accepts a WindowGlobalInit for a BrowsingContext not owned by the sender
Categories
(Core :: DOM: Navigation, defect)
Tracking
()
People
(Reporter: iifizzyx, Assigned: nika)
References
(Blocks 1 open bug)
Details
(Keywords: csectype-site-isolation, reporter-external, sec-moderate, Whiteboard: [client-bounty-form][adv-main153+])
Attachments
(4 files, 2 external files)
Tested build
- Version: Firefox Nightly
154.0a1 - BuildID:
20260624061603 - Platform: Ubuntu 24.04.4 LTS, Linux
6.8.0-124-generic, x86_64 - Build config: default source build,
MOZCONFIGunset - Runtime config: fresh profile, no preference overrides, sandbox/e10s-disabling environment variables unset
Summary
A compromised content process can send PBrowser::NewWindowGlobal through one BrowserChild while placing another same-group BrowsingContext ID in WindowGlobalInit.context().mBrowsingContextId.
BrowserParent::RecvNewWindowGlobal verifies that the target context exists and that its BrowsingContextGroup is known to the child process, but it does not reject a target CanonicalBrowsingContext that is not owned by the sending ContentParent. WindowGlobalParent::CreateDisconnected then resolves the foreign target from the supplied ID, and WindowGlobalParent::Init() installs the attacker-owned actor as the target context's current window global.
The PoC validates this on default Nightly by making an attacker-owned WindowGlobalParent current for a victim top-level BrowsingContext:
The marker name fakeEndpoint means a separate forged PWindowGlobal endpoint created by the diagnostic to model a compromised content process constructing an extra actor with fresh window IDs. It does not mean the disclosed restore or login data is fake; those values come from normal sessionstore and password-manager storage prepared in the test profile.
[WGP-XBC child] source=17179869185 target=4 sourceInProcess=1 targetInProcess=0 targetingTop=1 fakeEndpoint=1 forgedDocURI=http://127.0.0.1:8000/top-target-source.html
[WGP-XBC fake-child] source=17179869185 target=4 realInner=17179869187 fakeInner=17179869188 fakeOuter=17179869189 targetingTop=1
[WGP-XBC parent-recv] receiverBC=6442450945 targetBC=4 ownedBySender=0 sameAsReceiver=0
[WGP-XBC active] targetBC=4 ownerProcess=3 targetContentProcess=3 actorContentProcess=8 isCurrent=1 isActiveInTab=1 actorBrowserIsReceiver=1 crossManager=1
[WGP-XBC parent-accepted] targetBC=4 inner=17179869188 ownedBySenderBeforeInit=0
Impact
This is not only state corruption. The corrupted currentWindowGlobal is consumed by real parent subsystems and routes restricted browser data to the unauthorized actor.
Validated impact 1: real saved sessionstore form data is sent to the attacker-owned child actor. The profile was generated with normal sessionstore-backups/recovery.jsonlz4 crash-restore state containing a saved victim-frame form field:
victim_secret = WGP-XBC-REAL-RESTORE-SECRET-20260624
The parent selected restore data for victim targetBC=12884901889, whose owner process was 8, and delivered/deserialized it through the forged actor in process 6:
[WGP-XBC fake-child] source=12884901890 target=12884901889 realInner=12884901893 fakeInner=12884901894 fakeOuter=12884901895 targetingTop=0
[WGP-XBC restore-early-parent] targetBC=12884901889 topBC=11 gotPath=1 pathLength=1 hasRestoreData=1 wgpDocURI=http://127.0.0.2:8000/target.html?static_target=1
[WGP-XBC restore-ipc-read-entry] isXPath=0 id=victim_secret value=WGP-XBC-REAL-RESTORE-SECRET-20260624
[WGP-XBC active] targetBC=12884901889 ownerProcess=8 targetContentProcess=8 actorContentProcess=6 isCurrent=1 isActiveInTab=1 actorBrowserIsReceiver=1 crossManager=1
[WGP-XBC restore-request-parent] bc=12884901889 ownerProcess=8 actorProcess=6 ancestorsCurrent=1 hasRestoreData=1
[WGP-XBC restore-child] childBC=12884901889 restoreUrl=http://127.0.0.2:8000/target.html?static_target=1
Validated impact 2: saved-login credentials for the victim/top origin are selected by the parent password manager and delivered to the attacker-owned child actor. The validation selects the login origin from windowGlobal.documentURI, proving the parent selected the victim/top document origin http://127.0.0.1:8000, not the source-frame origin:
[WGP-XBC login-probe-parent-prepared] origin=http://127.0.0.1:8000 username=wgp_xbc_login_user password=WGP-XBC-LOGIN-SECRET-20260624
[WGP-XBC child] source=17179869185 target=10 sourceInProcess=1 targetInProcess=0 targetingTop=1 fakeEndpoint=1 forgedDocURI=http://127.0.0.1:8000/top-target-source.html
[WGP-XBC fake-child] source=17179869185 target=10 realInner=17179869187 fakeInner=17179869188 fakeOuter=17179869189 targetingTop=1
[WGP-XBC active] targetBC=10 ownerProcess=5 targetContentProcess=5 actorContentProcess=8 isCurrent=1 isActiveInTab=1 actorBrowserIsReceiver=1 crossManager=1
[WGP-XBC login-probe-parent-send] targetBC=10 actorBC=10 origin=http://127.0.0.1:8000 username=wgp_xbc_login_user password=WGP-XBC-LOGIN-SECRET-20260624
[WGP-XBC login-probe-child] childBC=10 loginFormOrigin=http://127.0.0.1:8000 username=wgp_xbc_login_user password=WGP-XBC-LOGIN-SECRET-20260624
A fresh 10-run stress pass reproduced the corrected saved-login disclosure every time:
docuri_success_child_markers=10/10
wrong_origin_child_markers=0/10
abort_markers=0/10
crash_markers=0/10
profiles_with_minidumps=0/10
This demonstrates parent-to-less-privileged-process disclosure of saved browser data in the compromised-content-process threat model. I am not claiming parent/native code execution.
Attached files
report.md: issue summary, impact, reproduction, and fix guidance.root_cause.md: root-cause analysis and invariant violation.validation.log: tested build, commands, and fresh validation markers.poc.patch: focused diagnostic patch forc1395f54bdda7cb0ed71bbe54e2940ce16175389.run_wgp_xbc_poc.sh: self-contained runner that creates the temporary local site and restore profile, appliespoc.patchif needed, builds, and runs the top-WGP, restore-data, and saved-login validations.
Reproduction
Run from the submission directory:
./run_wgp_xbc_poc.sh /path/to/firefox-source all
The source checkout should be at commit c1395f54bdda7cb0ed71bbe54e2940ce16175389.
The runner uses env -u to explicitly remove environment variables that would disable sandboxing/e10s; it does not disable those protections.
| Reporter | ||
Comment 1•4 months ago
|
||
| Reporter | ||
Comment 2•4 months ago
|
||
| Reporter | ||
Comment 3•4 months ago
|
||
Updated•4 months ago
|
Updated•4 months ago
|
Comment 4•4 months ago
|
||
Nika, do you know why bug 2038695 isn't enough for this?
| Assignee | ||
Comment 5•3 months ago
|
||
I thought I had already made this change, but apparently not. I've definitely written this code before, but it must not have landed.
This is just a small extra check to make sure that the BrowsingContext can be managed by the specific PBrowser actor.
I think this is the only call-site where a check like this needs to be added.
| Assignee | ||
Comment 6•3 months ago
|
||
Updated•3 months ago
|
Updated•3 months ago
|
| Assignee | ||
Comment 7•3 months ago
|
||
Original Revision: https://phabricator.services.mozilla.com/D309680
Updated•3 months ago
|
Comment 8•3 months ago
|
||
firefox-beta Uplift Approval Request
- User impact if declined/Reason for urgency: Sandboxing IPC boundary violation which could lead to strange behaviour.
- 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: Straightforward small fix.
- String changes made/needed?: None
- Is Android affected?: yes
Comment 10•3 months ago
|
||
Updated•3 months ago
|
Updated•3 months ago
|
Updated•3 months ago
|
Comment 11•3 months ago
|
||
| uplift | ||
Updated•3 months ago
|
Updated•3 months ago
|
Updated•3 months ago
|
Updated•1 month ago
|
Updated•1 month ago
|
Description
•