841ad59485b9991f47dd40dc1c9652bc714f7231 WebKit NetworkProcess: CORS bypass via unvalidated SetCORSDisablingPatterns IPC
Triage note: Removes the NetworkConnectionToWebProcess::setCORSDisablingPatterns IPC handler and its .messages.in entry, instead applying patterns only from trusted UIProcess-supplied parameters at connection creation; previously a compromised web process could send arbitrary CORS-disabling patterns to the network process and defeat same-origin enforcement.
Contents
The bug at a glance
The vulnerable IPC is exposed to the WebContent process, which is the standard post-renderer-compromise attacker position in WebKit’s threat model, so this is a second-stage sandbox-escape-adjacent capability rather than a pure remote bug. A compromised WebContent process could send SetCORSDisablingPatterns with a pattern like *://*/* to the NetworkProcess and defeat same-origin enforcement, letting it read the body of any cross-origin site the user is authenticated to (SOP/CORS bypass, confidentiality-critical). High/8.1 reflects the requirement of an already-compromised renderer combined with the severe cross-origin data-theft impact and no user interaction beyond browsing.
CORS-disabling patterns are a legitimate embedder feature (_corsDisablingPatterns SPI, WebExtensions), but the plumbing let the NetworkProcess accept those patterns from the WebContent process over Messages::NetworkConnectionToWebProcess::SetCORSDisablingPatterns. That put an untrusted, potentially-compromised renderer directly in the trust path for a decision — “disable the same-origin policy for these URLs” — that only the trusted UIProcess is entitled to make. A compromised renderer could simply send the message with *://*/* and turn the NetworkProcess into an open cross-origin proxy for the user’s authenticated sessions. The fix deletes the WebContent-process IPC entirely and re-routes the patterns from the UIProcess straight to the NetworkProcess, with a fallback that ships them inside the connection-creation parameters when the NetworkProcess isn’t up yet.
Root cause
The vulnerable state is the network-side enforcement data consulted by NetworkProcess::shouldDisableCORSForRequestTo: the per-page CORS-disabling patterns and the per-connection originAccessPatterns(). Before the patch, that state could be written by the WebContent process. NetworkConnectionToWebProcess::setCORSDisablingPatterns(PageIdentifier, Vector<String>&&) was an IPC handler (declared in NetworkConnectionToWebProcess.messages.in as SetCORSDisablingPatterns(WebCore::PageIdentifier pageIdentifier, Vector<String> patterns)) that forwarded straight to m_networkProcess->setCORSDisablingPatterns(*this, pageIdentifier, ...).
The reaching path from an attacker: a compromised WebContent process crafts and sends Messages::NetworkConnectionToWebProcess::SetCORSDisablingPatterns(pageID, {"*://*/*"}) on its existing NetworkProcessConnection. The handler parsed each string as a UserContentURLPattern and, for valid patterns, called connection.originAccessPatterns().allowAccessTo(parsedPattern) and stored them for the page — so subsequent cross-origin fetches from that renderer would pass shouldDisableCORSForRequestTo for arbitrary origins. Nothing validated that the renderer was actually entitled to those patterns; the values were entirely attacker-chosen.
This is unsafe because in WebKit’s process model the WebContent process is untrusted (it runs web content and is the assumed-compromised party), whereas CORS-disabling is a privileged embedder configuration that originates in the UIProcess as _corsDisablingPatterns. Legitimately the flow was UIProcess -> WebContent -> NetworkProcess, and WebPage::synchronizeCORSDisablingPatternsWithNetworkProcess() sent the IPC on the renderer’s behalf; but because the message crossed a trust boundary through the untrusted middle hop, a compromised renderer could substitute any patterns it wanted. The result is a full same-origin-policy / CORS bypass usable to exfiltrate authenticated cross-origin content.
The fix removes the WebContent process from the trust path. It deletes the SetCORSDisablingPatterns message and handler (and the header decl), deletes WebPage::synchronizeCORSDisablingPatternsWithNetworkProcess() and its callers in WebPage’s constructor/destructor/updateCORSDisablingPatterns and in WebProcess::ensureNetworkProcessConnection. In their place, the UIProcess sends patterns directly: WebPageProxy::setCORSDisablingPatterns and the new sendCORSDisablingPatternsToNetworkProcessIfNecessary() send Messages::NetworkProcess::SetCORSDisablingPatternsForPage(processIdentifier, pageID, patterns) straight to the trusted NetworkProcess, finishAttachingToWebProcess replays them across process swaps, and for the not-yet-launched case the patterns ride along in NetworkProcessConnectionParameters::corsDisablingPatternsPerPage, applied in NetworkProcess::createNetworkConnectionToWebProcess. The renderer still receives Messages::WebPage::UpdateCORSDisablingPatterns only to populate its own WebCore-side OriginAccessPatternsForWebProcess singleton, which governs same-process checks and is not authoritative for the NetworkProcess.
Key code
Removed renderer-facing message/handler and the new UIProcess-direct enforcement path
// NetworkConnectionToWebProcess.messages.in (DELETED — renderer could send this):
- SetCORSDisablingPatterns(WebCore::PageIdentifier pageIdentifier, Vector<String> patterns)
// NetworkConnectionToWebProcess.cpp (DELETED handler):
-void NetworkConnectionToWebProcess::setCORSDisablingPatterns(PageIdentifier pageIdentifier, Vector<String>&& patterns)
-{ m_networkProcess->setCORSDisablingPatterns(*this, pageIdentifier, WTF::move(patterns)); }
// NetworkProcess.cpp (new UIProcess-facing message target):
void NetworkProcess::setCORSDisablingPatternsForPage(WebCore::ProcessIdentifier webProcessIdentifier, PageIdentifier pageIdentifier, Vector<String>&& patterns)
{
auto parsedPatterns = WTF::compactMap(WTF::move(patterns), [&](auto&& pattern) -> std::optional<UserContentURLPattern> {
UserContentURLPattern parsedPattern(WTF::move(pattern));
if (!parsedPattern.isValid())
return std::nullopt;
if (RefPtr connection = webProcessConnection(webProcessIdentifier))
connection->originAccessPatterns().allowAccessTo(parsedPattern);
return parsedPattern;
});
...
}
// WebPageProxy.cpp (UIProcess sends directly to NetworkProcess):
void WebPageProxy::sendCORSDisablingPatternsToNetworkProcessIfNecessary()
{
if (m_corsDisablingPatterns.isEmpty()) return;
RefPtr networkProcess = websiteDataStore().networkProcessIfExists();
if (!networkProcess) return;
networkProcess->send(Messages::NetworkProcess::SetCORSDisablingPatternsForPage(
legacyMainFrameProcess().coreProcessIdentifier(), webPageIDInMainFrameProcess(), m_corsDisablingPatterns), 0);
}
Patch walkthrough
Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.cpp / .h / .messages.in— Deletes thesetCORSDisablingPatternshandler, its private declaration, and theSetCORSDisablingPatterns(WebCore::PageIdentifier, Vector<String>)entry in the WebContent-facing.messages.in. This removes the untrusted renderer’s ability to push CORS-disabling patterns to the NetworkProcess at all — the core of the fix, closing the attack surface.Source/WebKit/NetworkProcess/NetworkProcess.cpp / .h / .messages.in— RenamessetCORSDisablingPatterns(NetworkConnectionToWebProcess&, ...)tosetCORSDisablingPatternsForPage(WebCore::ProcessIdentifier, PageIdentifier, Vector<String>&&)and exposes it as a new UIProcess-facing messageSetCORSDisablingPatternsForPage. The reworked body looks up the connection viawebProcessConnection(webProcessIdentifier)(rather than trusting a caller-supplied connection) to populateoriginAccessPatterns(), with a comment stating a compromised WebContent process must not be able to disable CORS.createNetworkConnectionToWebProcessnow appliesparameters.corsDisablingPatternsPerPageat connection-creation time.Source/WebKit/Shared/NetworkProcessConnectionParameters.h / .serialization.in— AddsHashMap<WebCore::PageIdentifier, Vector<String>> corsDisablingPatternsPerPageto the connection parameters (and its serialization entry), plus the needed PageIdentifier/HashMap includes. This is the trusted channel for delivering patterns when the NetworkProcess has not yet been launched, so no renderer IPC is needed to bootstrap them.Source/WebKit/UIProcess/Network/NetworkProcessProxy.cpp— IngetNetworkProcessConnection, for each main page with non-emptycorsDisablingPatterns(), populatesparameters.corsDisablingPatternsPerPagekeyed bywebPageIDInMainFrameProcess(). This wires the UIProcess’s authoritative pattern list into the connection-creation parameters.Source/WebKit/UIProcess/WebPageProxy.cpp / .h—setCORSDisablingPatternsnow also calls the newsendCORSDisablingPatternsToNetworkProcessIfNecessary(), which (if patterns are non-empty and a NetworkProcess exists) sendsMessages::NetworkProcess::SetCORSDisablingPatternsForPage(legacyMainFrameProcess().coreProcessIdentifier(), webPageIDInMainFrameProcess(), m_corsDisablingPatterns)directly to the NetworkProcess.finishAttachingToWebProcesscalls the same helper so patterns are replayed after a process swap.Source/WebKit/WebProcess/WebPage/WebPage.cpp / .h and WebProcess.cpp— DeletesWebPage::synchronizeCORSDisablingPatternsWithNetworkProcess()and every call to it: in the WebPage constructor, the destructor’s clear-on-teardown path, andupdateCORSDisablingPatterns; also removes the RunLoop dispatch inWebProcess::ensureNetworkProcessConnectionthat resynchronized all pages’ patterns on connection setup. The WebContent process retains onlypage->setCORSDisablingPatterns(parseAndAllowAccessToCORSDisablingPatterns(...))for its own in-process WebCore same-origin checks.
Background
WebKit process trust model — The WebContent (renderer) process is treated as untrusted and assumed compromisable by web content; the UIProcess is the trusted arbiter of security policy; the NetworkProcess performs network I/O and enforces CORS/SOP. Privileged configuration must flow UIProcess -> NetworkProcess without depending on a renderer, which this patch enforces.
CORS-disabling patterns / _corsDisablingPatterns — An embedder SPI (also used by WebExtensions) that whitelists URL patterns for which the same-origin policy is relaxed, letting an app fetch cross-origin resources. UserContentURLPattern parses entries like *://*/*; the NetworkProcess consults them in shouldDisableCORSForRequestTo. Because relaxing SOP is a powerful capability, its source must be trusted.
originAccessPatterns() / OriginAccessPatternsForWebProcess — The NetworkProcess keeps per-connection originAccessPatterns() used when deciding whether to disable CORS for a request. Separately, the WebContent process keeps its own OriginAccessPatternsForWebProcess singleton for WebCore-side same-origin checks; the patch still populates the latter via UpdateCORSDisablingPatterns but no longer lets it drive the NetworkProcess’s authoritative copy.
NetworkProcessConnectionParameters — A struct serialized when a WebProcess’s NetworkProcess connection is created. Adding corsDisablingPatternsPerPage lets the UIProcess-supplied patterns be applied at createNetworkConnectionToWebProcess time, closing the bootstrap gap where the NetworkProcess isn’t yet running to receive a direct message.
Vulnerability window
- Legitimate design — Embedder sets
_corsDisablingPatternsin UIProcess; patterns propagated UIProcess -> WebContent -> NetworkProcess viaWebPage::synchronizeCORSDisablingPatternsWithNetworkProcess()sendingNetworkConnectionToWebProcess::SetCORSDisablingPatterns. - Renderer compromise — An attacker exploits a separate renderer bug to run code in the WebContent process, gaining the ability to send arbitrary IPC on the existing NetworkProcessConnection.
- Abuse the IPC — The compromised renderer sends
SetCORSDisablingPatterns(pageID, {"*://*/*"}); the NetworkProcess handler accepts it, callsallowAccessTo, and stores the patterns for the page. - Cross-origin exfiltration — Subsequent fetches pass
shouldDisableCORSForRequestTofor arbitrary origins, so the renderer reads responses from any cross-origin site the user is authenticated to. - Fix — The renderer-facing message and sync code are deleted; patterns flow UIProcess -> NetworkProcess directly (via
SetCORSDisablingPatternsForPageand connection parameters), replayed across process swaps infinishAttachingToWebProcess.
Triggering
No PoC is reconstructable from the patch: there is no regression test in the diff, and triggering the bug requires an already-compromised WebContent process able to synthesize the Messages::NetworkConnectionToWebProcess::SetCORSDisablingPatterns(pageID, {"*://*/*"}) IPC on its NetworkProcessConnection — a native second-stage capability, not a scriptable web primitive. Conceptually, a compromised renderer sends that message and then issues cross-origin fetches that the NetworkProcess no longer subjects to CORS; but fabricating the renderer-compromise primitive is out of scope of this patch and no such primitive is provided.
Exploitation
- Obtain renderer code execution — Chain a separate WebContent-process vulnerability to run attacker-controlled native code inside the sandboxed renderer, which holds a live NetworkProcessConnection.
- Push permissive patterns — Send
SetCORSDisablingPatternswith a broad pattern (e.g.*://*/*) for the page identifier, causing the NetworkProcess to relax CORS for all origins on that page’s connection. - Steal authenticated cross-origin data — Issue cross-origin requests to sites where the user has cookies/credentials and read the responses that CORS would normally block, exfiltrating private data. This is a confidentiality breach, not memory corruption.
Detection & hunting
For defenders and SOC / detection engineers:
- Renderer-originated CORS-policy IPC —
- Unexpected origin-access relaxation —
- Cross-origin reads post-renderer-anomaly —
Audit directions
- Other NetworkConnectionToWebProcess.messages.in handlers —
- UIProcess-supplied identifiers —
- Process-swap and cold-start replay —