← All issues

[3] WebContent sandbox bypass for local file reads

Severity: High | Component: WebKit WebProcess loader / NetworkProcess resource load parameters | dfe2e0e

Rated High because the observable effect is arbitrary local file reads from the WebContent process via the Networking process — the sandbox extension creation failure was silently ignored, and the Networking process's broader file access is a well-established architectural property of WebKit's multi-process model.

The WebContent process should not start local file loads for files it cannot access. Before this fix, it could, because the Networking process would service the load regardless.

Source/WebKit/NetworkProcess/NetworkResourceLoadParameters.cpp

- void NetworkResourceLoadParameters::createSandboxExtensionHandlesIfNecessary()
+ bool NetworkResourceLoadParameters::createSandboxExtensionHandlesIfNecessary()
{
...
if (request.url().protocolIsFile()) {
...
if (auto handle = SandboxExtension::createHandle(request.url().fileSystemPath(), SandboxExtension::Type::ReadOnly))
resourceSandboxExtension = WTF::move(*handle);
}
+ return resourceSandboxExtension.has_value();
}
+ return true;
}

Source/WebKit/WebProcess/Network/WebLoaderStrategy.cpp

- loadParameters.createSandboxExtensionHandlesIfNecessary();
+ if (!loadParameters.createSandboxExtensionHandlesIfNecessary()) {
+ RunLoop::mainSingleton().dispatch([resourceLoader = Ref { resourceLoader }, error = blockedError(request)] {
+ resourceLoader->didFail(error);
+ });
+ return;
+ }

The fix changes createSandboxExtensionHandlesIfNecessary() from void to bool, returning false when the request is a file:// URL and the WebContent process fails to create a sandbox extension handle. In WebLoaderStrategy::scheduleLoadFromNetworkProcess, the return value is now checked — if false, the load is immediately failed with a blockedError before the request reaches the Networking process.

Missing access-control enforcement at the IPC boundary between WebContent and Networking processes for local file loads.

WebKit's multi-process architecture splits work between a sandboxed WebContent process (which renders pages) and a Networking process (which handles network and file I/O). The WebContent process has a restrictive sandbox that limits file system access. When the WebContent process needs to load a file:// URL, it must create a SandboxExtension handle — a capability token granting the Networking process permission to read that specific file on the WebContent process's behalf. SandboxExtension::createHandle succeeds only if the calling process itself has access to the file. The Networking process runs with broader file system access than the WebContent process, as it must handle downloads, cookie storage, and cache files across all origins.

This is a classic confused-deputy pattern at an IPC boundary. Before the fix, createSandboxExtensionHandlesIfNecessary() returned void. When the WebContent process requested a file:// URL it could not access, SandboxExtension::createHandle would fail and resourceSandboxExtension would remain empty — but the load proceeded anyway, forwarding the request to the Networking process without a sandbox extension. The Networking process, which runs with broader file system access, would then read the file and return its contents to the WebContent process. The access check existed in form (the extension creation was attempted) but its failure was silently ignored.

🔒

Explores the cross-process trust model and how silent failure at a sandbox boundary creates an exploitable file-read primitive

Subscribe to read more

🔒

Multiple audit patterns identified around IPC sandbox extension validation, with concrete call sites and search targets

Subscribe to read more