PExternalHelperApp trusts forged file: URI state for parent-side helper launch
Categories
(Core :: DOM: Content Processes, defect, P2)
Tracking
()
People
(Reporter: kq5y, Assigned: edenchuang)
References
Details
(Keywords: csectype-priv-escalation, reporter-external, sec-moderate, Whiteboard: [client-bounty-form][adv-main153+][adv-ESR140.13+])
Attachments
(5 files)
|
6.60 KB,
patch
|
Details | Diff | Splinter Review | |
|
17.63 KB,
application/octet-stream
|
Details | |
|
48 bytes,
text/x-phabricator-request
|
Details | Review | |
|
48 bytes,
text/x-phabricator-request
|
phab-bot
:
approval-mozilla-beta+
|
Details | Review |
|
48 bytes,
text/x-phabricator-request
|
phab-bot
:
approval-mozilla-esr140+
|
Details | Review |
Overview:
Under the compromised-content-process IPC attacker model, PExternalHelperApp lets the child provide the URI, LoadInfoArgs, MIME type, disposition metadata, and aWasFileChannel value used to initialize a parent-side ExternalHelperAppParent.
A compromised content process can send a file: URI with aWasFileChannel=true and then send OnStartRequest. If the selected MIME handling action reaches open-with-helper or system-default handling without prompting, the parent treats the request as a file channel and reaches nsExternalAppHandler::LaunchLocalFile() / nsMIMEInfo::LaunchWithFile() for the child-chosen local path.
The security boundary issue is that the parent derives the native helper-launch decision from child-controlled channel identity rather than binding it to a parent-created, parent-authorized channel.
This is not reachable from ordinary web JavaScript alone. The attached PoC simulates a compromised content process by modifying the child side to send a forged PExternalHelperApp constructor when a test pref is enabled.
I am not claiming arbitrary parent-process code execution, UXSS, arbitrary file read, arbitrary file write, or a complete sandbox escape from the attached PoC. I am submitting this for Mozilla to evaluate under the compromised-content-process / IPC trust-boundary model. The demonstrated primitive is parent-mediated helper-app launch of a child-chosen local file path, not full arbitrary native code execution.
Tested build:
- Firefox current main / Nightly 152.0a1
- Source revision: 357c433d03654a1b82ac3ab44b95aa9842e2dbe2
- Build ID: 20260514040943
- Platform used for validation: macOS 26.4.1 arm64
Steps to reproduce:
-
In a Firefox source checkout with normal Firefox build dependencies installed, apply the attached patch:
git apply /path/to/pexternalhelperapp-forged-file-launch.patch
-
Create a harmless local target file:
: > /tmp/bbp-external-helper-poc.noopen
-
Build Firefox:
./mach build
-
Run the PoC test:
BBP_EXTERNAL_HELPER_DIRECT=1 BBP_EXTERNAL_HELPER_FILE_URI=file:///tmp/bbp-external-helper-poc.noopen ./mach mochitest uriloader/exthandler/tests/mochitest/browser_bbp_external_helper_forged_file_launch_poc.js --headless --force
Actual results:
The test completes the forged child-to-parent PExternalHelperApp flow. The parent-side launch stub observes the child-chosen local file path and blocks the real native launch.
Relevant output from the separate mochitest-current-main.log attachment:
PASS forged_external_helper_app_ipc_launches_file_url - content process is ready to receive the trigger pref - true == true
GECKO(...) BBP_EXTERNAL_HELPER_DIRECT: sent uri=file:///tmp/bbp-external-helper-poc.noopen wasFileChannel=1
PASS forged_external_helper_app_ipc_launches_file_url - parent process attempted to launch the child-forged file:// URI
TEST_END: Test PASS. Subtests passed 3/3. Unexpected 0
Overall Summary
Expected results: 4
Unexpected results: 0
OK
Expected results:
The parent process should not trust a content-process supplied file: URI or aWasFileChannel=true value for PExternalHelperApp. A compromised content process should not be able to make the parent invoke the native helper-launch path for an arbitrary local path unless the parent can bind that request to a real, already-authorized channel.
Root cause:
- dom/ipc/PContent.ipdl: PExternalHelperApp accepts child-supplied uri, LoadInfoArgs, MIME type, disposition fields, and aWasFileChannel.
- dom/ipc/ContentParent.cpp: AllocPExternalHelperAppParent stores uri and aWasFileChannel in ExternalHelperAppParent.
- dom/ipc/ContentParent.cpp: RecvPExternalHelperAppConstructor calls ExternalHelperAppParent::Init without validating that the supplied uri/aWasFileChannel pair matches a real channel authorized for the sending content process.
- uriloader/exthandler/ExternalHelperAppParent.h: WasFileChannel() returns the constructor-supplied mWasFileChannel.
- uriloader/exthandler/nsExternalHelperAppService.cpp: OnStartRequest trusts parent->WasFileChannel() when the fake channel is an ExternalHelperAppParent, then selects LaunchLocalFile() when the MIME action is an open action.
- uriloader/exthandler/nsExternalHelperAppService.cpp: LaunchLocalFile() converts mSourceUrl to nsIFileURL and calls mMimeInfo->LaunchWithFile(file), reaching the platform-native helper launch path.
| Reporter | ||
Comment 1•5 months ago
|
||
| Reporter | ||
Updated•5 months ago
|
| Reporter | ||
Updated•5 months ago
|
Updated•5 months ago
|
Comment 3•4 months ago
|
||
The severity field is not set for this bug.
:edenchuang, could you have a look please?
For more information, please visit BugBot documentation.
Updated•4 months ago
|
| Assignee | ||
Comment 4•4 months ago
|
||
Could someone add me in bug 2025890 cc list to ensure if these two are the same or similar issue that can be fixed together?
Comment 5•4 months ago
|
||
(In reply to Eden Chuang[:edenchuang] from comment #4)
Could someone add me in bug 2025890 cc list to ensure if these two are the same or similar issue that can be fixed together?
Done.
Updated•3 months ago
|
| Assignee | ||
Comment 8•3 months ago
|
||
I took some time to identify this bug and bug 2025890. I think we can mark 2025890 as a dup of this.
They both agree on the actual security defect: the parent derives the file-channel launch decision from a content-process-controlled boolean (aWasFileChannel) and a content-supplied file: URI, instead of binding it to a parent-authorized channel.
| Assignee | ||
Comment 10•3 months ago
|
||
The parent derived the native helper-launch decision from a
content-process supplied aWasFileChannel flag and URI rather than
binding it to the sending process's capabilities.
Stop sending aWasFileChannel over IPC and derive it on the parent from
the actual URI, and reject a file:// URI in PExternalHelperApp from a
process that is not allowed to load local files (mirrors the file://
policy in ValidatePrincipalCouldPotentiallyBeLoadedBy).
Updated•3 months ago
|
Comment 11•3 months ago
|
||
Comment 12•3 months ago
|
||
Comment 13•3 months ago
|
||
Please add a beta uplift request
Updated•3 months ago
|
Comment 14•3 months ago
|
||
firefox-beta Uplift Approval Request
- User impact if declined/Reason for urgency: A compromised content process can send a file: URI with aWasFileChannel=true and then send OnStartRequest. If the selected MIME handling action reaches open-with-helper or system-default handling without prompting, the parent treats the request as a file channel and reaches nsExternalAppHandler::LaunchLocalFile() / nsMIMEInfo::LaunchWithFile() for the child-chosen local path.
- Code covered by automated testing?: no
- Fix verified in Nightly?: no
- Needs manual QE testing?: no
- Steps to reproduce for manual QE testing:
- Risk associated with taking this patch: low
- Explanation of risk level: The patch removes the aWasFileChannel argument from the IPC, so the parent side determines whether it is a file channel based on the process type and the URI. So the risk is low.
- String changes made/needed?: no
- Is Android affected?: yes
| Assignee | ||
Comment 15•3 months ago
|
||
The parent derived the native helper-launch decision from a
content-process supplied aWasFileChannel flag and URI rather than
binding it to the sending process's capabilities.
Stop sending aWasFileChannel over IPC and derive it on the parent from
the actual URI, and reject a file:// URI in PExternalHelperApp from a
process that is not allowed to load local files (mirrors the file://
policy in ValidatePrincipalCouldPotentiallyBeLoadedBy).
Original Revision: https://phabricator.services.mozilla.com/D309577
Comment 16•3 months ago
|
||
firefox-esr140 Uplift Approval Request
- User impact if declined/Reason for urgency: A compromised content process can send a file: URI with aWasFileChannel=true and then send OnStartRequest. If the selected MIME handling action reaches open-with-helper or system-default handling without prompting, the parent treats the request as a file channel and reaches nsExternalAppHandler::LaunchLocalFile() / nsMIMEInfo::LaunchWithFile() for the child-chosen local path.
- Code covered by automated testing?: no
- Fix verified in Nightly?: no
- Needs manual QE testing?: no
- Steps to reproduce for manual QE testing:
- Risk associated with taking this patch: low
- Explanation of risk level: The patch removes the aWasFileChannel argument from the IPC, so the parent side determines whether it is a file channel based on the process type and the URI. So the risk is low.
- String changes made/needed?: no
- Is Android affected?: yes
| Assignee | ||
Comment 17•3 months ago
|
||
The parent derived the native helper-launch decision from a
content-process supplied aWasFileChannel flag and URI rather than
binding it to the sending process's capabilities.
Stop sending aWasFileChannel over IPC and derive it on the parent from
the actual URI, and reject a file:// URI in PExternalHelperApp from a
process that is not allowed to load local files (mirrors the file://
policy in ValidatePrincipalCouldPotentiallyBeLoadedBy).
Original Revision: https://phabricator.services.mozilla.com/D309577
Updated•3 months ago
|
Updated•3 months ago
|
Updated•3 months ago
|
Comment 18•3 months ago
|
||
| uplift | ||
Updated•3 months ago
|
Updated•3 months ago
|
Comment 19•3 months ago
|
||
| uplift | ||
Updated•3 months ago
|
| Assignee | ||
Updated•3 months ago
|
Updated•2 months ago
|
Updated•2 months ago
|
Comment 21•1 month ago
|
||
Although this is the bug we fixed it in, this problem had already been reported in the dupe bug 2025890
Updated•26 days ago
|
Description
•