Forged PExternalHelperApp file-channel state lets a compromised content process make the parent open a local file
Categories
(Core :: DOM: Content Processes, defect)
Tracking
()
People
(Reporter: 5up3rh3i, Unassigned)
Details
(Keywords: ai-involved, reporter-external, Whiteboard: [client-bounty-form])
Attachments
(1 file)
|
8.37 KB,
application/zip
|
Details |
Summary
A compromised content process can send a forged PExternalHelperApp
constructor to the parent process with an attacker-chosen file:// URI and
aWasFileChannel=true.
The parent trusts this child-controlled flag. Later,
nsExternalAppHandler::OnStartRequest() treats the request as a local file
channel and calls LaunchLocalFile(), which launches the file from mSourceUrl.
The implementation shows that LaunchLocalFile() resolves the file://
source URL to an nsIFile and calls mMimeInfo->LaunchWithFile(file).
The platform sinks are real external-launch sinks rather than a bookkeeping
flag:
macOS: nsMIMEInfoMac::LaunchWithFile()
-> LSGetApplicationForItem(...)
-> app->LaunchWithDoc(aFile, false)
generic: nsMIMEInfoBase::LaunchWithFile()
-> LaunchDefaultWithFile(aFile) or LaunchWithIProcess(...)
Windows: nsMIMEInfoWin::LaunchWithFile()
-> ShellExecuteWithIFile(...)
Root Cause
The child computes whether the original channel was an nsIFileChannel and
sends aWasFileChannel to the parent. The parent does not recompute or validate
this property; it stores the value in ExternalHelperAppParent and later uses
it to decide whether to call LaunchLocalFile().
Relevant current-source path:
ContentParent::AllocPExternalHelperAppParent(..., aWasFileChannel, ...)
-> ExternalHelperAppParent(..., aWasFileChannel)
-> ExternalHelperAppParent::Init(...)
-> nsExternalHelperAppService::DoContent(...)
-> nsExternalAppHandler::OnStartRequest(...)
-> mIsFileChannel ? LaunchLocalFile() : SetDownloadToLaunch(...)
-> nsExternalAppHandler::LaunchLocalFile()
-> mMimeInfo->LaunchWithFile(file)
The automatic launch branch is conditional on the ordinary helper-app handling
state. Current source computes alwaysAsk and the MIME preferred action before
opening:
nsExternalAppHandler::OnStartRequest(...)
-> mMimeInfo->GetAlwaysAskBeforeHandling(&alwaysAsk)
-> mMimeInfo->GetPreferredAction(&action)
-> if alwaysAsk:
mDialog->Show(...)
else if action is useHelperApp / useSystemDefault / handleInternally:
mIsFileChannel ? LaunchLocalFile() : SetDownloadToLaunch(...)
The forged aWasFileChannel flag controls the last decision: when the helper
configuration says the file should be opened automatically, the parent opens the
child-selected source file directly instead of treating it as a downloaded temp
file.
Broken invariant:
A child process must not decide whether the parent treats a helper-app request
as an already-local file channel.
Impact
A compromised content process gets a parent-process file-open primitive:
- parent process opens a child-selected local file path;
- normal sandbox restrictions preventing the content process from launching
local files are bypassed; - impact depends on MIME handler configuration and installed handlers.
This is a parent/OS helper boundary issue. The child process cannot normally
ask the parent to launch an arbitrary local path as an already-local helper-app
download, but the forged aWasFileChannel flag moves the request into that
branch.
The security impact is strongest when a local file type has a registered
default handler with side effects. Even when the default handler is benign, the
bug still gives a compromised content process a parent-mediated OS file-launch
primitive that is outside the web/content sandbox capability model.
The impact is configuration dependent in the same way as normal helper-app
opening: if Firefox is configured to always ask for that MIME type, the forged
request reaches the trusted helper-app dialog path first. If the MIME type is
configured for automatic helper/system handling, the forged local-file state
selects the direct parent LaunchLocalFile() path.
Security Impact
Launching local files or helper applications is outside the normal web content
sandbox. The parent process is the trusted component that decides whether a
download came from a local file channel and whether an OS helper should be
invoked. This bug lets a compromised content process forge that local-file
state and move a child-selected file URL into the parent helper-app launch path.
The exact downstream effect depends on MIME and OS handler configuration, but
the parent-mediated local-file launch primitive is the security boundary.
Reproduction
Observed:
EXTERNAL_HELPER_DIRECT_FORGE_SEND path=/var/folders/.../T/external-helper-forged-target.pdf
PASS forged_was_file_channel_opens_child_supplied_file - forged PExternalHelperApp parameters made the parent open the child-supplied local file
TEST_END: Test PASS. Subtests passed 1/1. Unexpected 0
The test observer prevented an actual external launch while recording the exact
path the parent attempted to open.
Source-confirmed final sink:
rv = mMimeInfo->LaunchWithFile(file);
Suggested Fix
Do not trust aWasFileChannel from the child process.
Recommended directions:
- Derive local-file status from parent-owned channel state.
- Reject inconsistent constructor fields.
- Validate that a local-file launch is tied to an authorized initiating context.
Scope
Confirmed behavior:
- forged IPC can make the parent enter the local-file launch path for a
child-selected file URL; - current source resolves the forged
file://source to annsIFileand calls
mMimeInfo->LaunchWithFile(file); - platform implementations hand the file to the OS/default handler path
(LaunchWithDoc,LaunchDefaultWithFile/LaunchWithIProcess, or
ShellExecuteWithIFile); - the PoC recorded the exact launch target for the selected MIME type,
while the test observer prevented the external launch.
Proof of Concept
The proof-of-concept files are provided under poc/.
Observed output:
Finished reading 2681 moz.build files in 1.46s
Observed output:
0:01.72 INFO runtests.py | Running with cross-origin iframes: False
0:03.10 GECKO(84931) [Parent 84931: Main Thread]: I/SlowChromeEvent Please do not use mouseenter/leave events in chrome. They are slower than mouseover/out! chrome://browser/content/tabbrowser/tabs.js:53:12
0:05.02 PASS forged_was_file_channel_opens_child_supplied_file - forged PExternalHelperApp parameters made the parent open the child-supplied local file
0:05.72 TEST_END: Test PASS. Subtests passed 1/1. Unexpected 0
Updated•3 months ago
|
Updated•3 months ago
|
Updated•3 months ago
|
Updated•23 days ago
|
Description
•