Closed Bug 2039452 (CVE-2026-16379) Opened 5 months ago Closed 3 months ago

PExternalHelperApp trusts forged file: URI state for parent-side helper launch

Categories

(Core :: DOM: Content Processes, defect, P2)

defect

Tracking

()

RESOLVED FIXED
154 Branch
Tracking Status
firefox-esr115 --- wontfix
firefox-esr140 153+ fixed
firefox152 --- wontfix
firefox153 + fixed
firefox154 + fixed

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)

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:

  1. In a Firefox source checkout with normal Firefox build dependencies installed, apply the attached patch:

    git apply /path/to/pexternalhelperapp-forged-file-launch.patch

  2. Create a harmless local target file:

    : > /tmp/bbp-external-helper-poc.noopen

  3. Build Firefox:

    ./mach build

  4. 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.
Flags: sec-bounty?
Group: firefox-core-security → core-security
Component: Security → DOM: Content Processes
Product: Firefox → Core
Attachment #9586232 - Attachment is patch: true
Attachment #9586232 - Attachment mime type: text/x-patch → text/plain
Group: core-security → dom-core-security
See Also: → 2025890

The severity field is not set for this bug.
:edenchuang, could you have a look please?

For more information, please visit BugBot documentation.

Flags: needinfo?(echuang)
See Also: → 2045736
Depends on: 2025890
See Also: 2025890 →

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?

Severity: -- → S3
Priority: -- → P2

(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.

Duplicate of this bug: 2045736
Duplicate of this bug: 2050484

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.

Flags: needinfo?(echuang)
Duplicate of this bug: 2025890
Attached file (secure) —

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).

Assignee: nobody → echuang
Status: UNCONFIRMED → ASSIGNED
Ever confirmed: true
Pushed by echuang@mozilla.com: https://github.com/mozilla-firefox/firefox/commit/7f4591110004 https://hg.mozilla.org/integration/autoland/rev/326bdeaff5ad Validate file:// URIs in PExternalHelperApp against the sending process's remote type. r=dom-core-reviewers,sfarre
Group: dom-core-security → core-security-release
Status: ASSIGNED → RESOLVED
Closed: 3 months ago
Resolution: --- → FIXED
Target Milestone: --- → 154 Branch

Please add a beta uplift request

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
Attachment #9607301 - Flags: approval-mozilla-beta?
Attached file (secure) —

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

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
Attachment #9607302 - Flags: approval-mozilla-esr140?
Attached file (secure) —

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

Attachment #9607301 - Flags: approval-mozilla-beta? → approval-mozilla-beta+
Attachment #9607302 - Flags: approval-mozilla-esr140? → approval-mozilla-esr140+
QA Whiteboard: [sec] [uplift] [qa-triage-done-c154/b153]
Flags: needinfo?(echuang)
Whiteboard: [client-bounty-form] → [client-bounty-form][adv-main153+][adv-ESR140.13+]
Alias: CVE-2026-16379

Although this is the bug we fixed it in, this problem had already been reported in the dupe bug 2025890

Flags: sec-bounty? → sec-bounty-
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: