[5] WebPageProxy::createNewPage missing sandbox-extension check on request httpBody
Name a file in a window.open request body, get a read token back
Rated High because the diff gates an IPC path where the UI process mints a live filesystem read token for a WebContent-supplied path; the granted capability is itself the sandbox escape for read, though reaching the path presupposes prior renderer compromise, which the change does not itself provide.
A compromised WebContent process can send WebPageProxy::CreateNewPage with an arbitrary file path in the request body's EncodedFileData. When the UI process re-emits that request via loadRequest(), encoding the LoadParameters runs FormDataReference::sandboxExtensionHandles() in the UI process, which calls SandboxExtension::createHandle() on the attacker-supplied path and serializes a live com.apple.app-sandbox.read token to a WebContent process — a full sandbox escape to arbitrary host filesystem read. The fix adds a MESSAGE_CHECK_COMPLETION at the top of createNewPage() that calls WebProcessProxy::hasGrantedSandboxExtensionForFile() on every EncodedFileData filename in the request body, rejecting any path the sending process was never granted access to. This mirrors the equivalent check that rdar://174662982 added to decidePolicyForNavigationAction().
Source/WebKit/UIProcess/WebPageProxy.cpp
LayoutTests/http/tests/ipc/createnewpage-file-body-sandbox-extension.html
Patch Details
The patch adds a capability check to WebPageProxy::createNewPage, the UI-process handler for the CreateNewPage IPC sent when script opens a new window. Per the commit message, it inserts a MESSAGE_CHECK_COMPLETION at the top of createNewPage() that iterates every FormDataElement::EncodedFileData filename in the incoming NavigationActionData's request body and calls WebProcessProxy::hasGrantedSandboxExtensionForFile() on each, rejecting the message if the sending process was never granted access to that path. The only production-code hunk visible in the diff is the added #include <WebCore/FormData.h> needed to walk the FormData element list — the body of the check is outside the truncated source window. The regression test drives CreateNewPage via the IPC testing API with a body element referencing /private/etc/passwd and asserts the message is rejected.
Confused-deputy sandbox escape from a UI-process IPC handler minting a filesystem capability token on an unvalidated, WebContent-supplied file path.
Background
A SandboxExtension is a WebKit capability handle by which the unsandboxed UI process grants a sandboxed process time-limited access to a specific host resource; SandboxExtension::createHandle(path, read) mints a token the receiving process can consume to open that path. com.apple.app-sandbox.read is the macOS sandbox extension class granting read access to a filesystem path. FormData/EncodedFileData is WebCore's representation of an HTTP request body — an EncodedFileData element references a file on disk by path (used for <input type=file> uploads). FormDataReference::sandboxExtensionHandles(), called when serializing LoadParameters, walks the body's file elements and produces sandbox extension handles so the target process can read the files it must upload. MESSAGE_CHECK/MESSAGE_CHECK_COMPLETION are WebKit macros in UI-process IPC handlers that validate an invariant on an incoming message and, on failure, reject the message (typically terminating the misbehaving process). WebProcessProxy::hasGrantedSandboxExtensionForFile() queries whether a given WebContent process was previously granted access to a path. Execution flow: script calls window.open → WebContent sends CreateNewPage with a NavigationActionData → UI process createNewPage processes it and eventually re-issues the navigation via loadRequest(), whose LoadParameters serialization runs sandboxExtensionHandles().
Analysis
The root cause is a confused-deputy / missing IPC capability check leading to sandbox escape. Before the fix, createNewPage trusted the file paths embedded in the NavigationActionData.request httpBody (FormData elements of type EncodedFileData) sent from WebContent without verifying that the sending process held a sandbox extension for those paths.
When the UI process re-emits the attacker-supplied request via loadRequest(), serializing the LoadParameters runs FormDataReference::sandboxExtensionHandles() in the UI process. For each EncodedFileData filename this calls SandboxExtension::createHandle(), which — because the UI process is unsandboxed with respect to the host filesystem — mints a live com.apple.app-sandbox.read token for the path and serializes it back down to the WebContent process, which consumes it to read a file it was never authorized to open. The missing invariant: the UI process must never mint a sandbox extension for a path on behalf of a WebContent process that was not previously granted access to it (e.g. via a file picker or drag-and-drop).
This is exploitable as a second-stage sandbox escape. From an already-compromised WebContent process, craft a CreateNewPage message whose NavigationActionData.request httpBody contains an EncodedFileData element naming a target path (the test uses /private/etc/passwd). The UI process re-emits the request, mints a live read token, and serializes it back to WebContent, which reads the file; iterating over paths yields arbitrary host-file read. It presupposes prior WebContent compromise — the CreateNewPage IPC is sent by the renderer, and reaching the payload requires either an existing renderer RCE or, as in the test, the IPC testing API. It is not a memory-corruption primitive; it is a direct capability/authorization bypass.
This is the relevant attack surface: the CreateNewPage IPC entry point already existed but lacked the file-path authorization gate its sibling decidePolicyForNavigationAction() received in the earlier rdar://174662982 fix. This reachable IPC path assumed — rather than enforced — that request-body file paths were already authorized for the sender; when that assumption broke, the UI process minted filesystem read tokens for arbitrary paths.
This vulnerability weakens the WebContent sandbox boundary. The security model assumes the UI process only grants WebContent a sandbox extension for a file after explicit user authorization; before the fix, a compromised renderer could bypass this by embedding an arbitrary path in a CreateNewPage request body, causing the UI process to hand back a live read token. Successful exploitation gives arbitrary host-filesystem read from within the compromised renderer — a full sandbox escape for read (exfiltrating /private/etc/passwd, cookies, or other users' data).
This is an explicit variant of the rdar://174662982 fix. The pattern — a UI-process IPC handler that forwards a WebContent-supplied request into a code path serializing FormDataReference::sandboxExtensionHandles() without re-checking file authorization — is a recurring class, because the token mint is centralized in sandboxExtensionHandles() but the authorization check is scattered per-handler and easy to omit.
Note: The exact fix body, the mint mechanism through loadRequest()/sandboxExtensionHandles(), the variant relationship to the prior fix, and the com.apple.app-sandbox.read class name are taken from the commit message — only the #include is visible in the diff. The confused-deputy shape and the rejection assertion in the test are directly supported.
Audit directions
- UIProcess handlers that accept a WebContent-supplied ResourceRequest/FormData and later serialize LoadParameters without validating file authorization become confused-deputy sandbox escapes. Audit every
WebPageProxyIPC handler that receives a request body and callsloadRequest/loadData; grepSource/WebKit/UIProcessfor message handlers takingNavigationActionData,ResourceRequest, orFormDataand cross-check each against ahasGrantedSandboxExtensionForFile()gate. - Verify the centralization gap.
FormDataReference::sandboxExtensionHandles()andSandboxExtension::createHandle()mint tokens unconditionally, relying on callers to pre-authorize paths. GrepSource/WebKitfor callers ofcreateHandle()reachable from a WebContent-originated message and confirm each has an upstreamMESSAGE_CHECKon path provenance. - Trace all
EncodedFileData/FormDataElementconsumption paths in the UIProcess. Any place a filename arrives from WebContent and flows toward file-access token creation should be treated as untrusted. ExamineWebPageProxy::loadRequest,decidePolicyForNavigationAction,decidePolicyForNavigationResponse, and download/upload paths for parity of enforcement. - Per-handler authorization checks tend to be forgotten on newly added or refactored IPC entry points. Review whether the check can be hoisted into
FormDataReferenceserialization or a shared validation helper so newCreateNewPage-like handlers inherit it by construction; investigateWebProcessProxyfor a single choke-point where granted-extension state could gate all mints.