Released October 28, 2024

CVE-2024-44212

CVSS 5.3 severity medium CWE-346 vector CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N

Overview

Impact:

Description: Cookies belonging to one origin may be sent to another origin

Researchers: Wojciech Regula of SecuRing (

Key insight. Classifying a web-archive load by the raw URL string (which includes query/fragment/encoding) instead of the filesystem path let an attacker hide or fake the .webarchive suffix, bypassing the ephemeral-store isolation and leaking cookies across origins.

Patch Details

diff --git a/Source/WebKit/UIProcess/WebPageProxy.cpp b/Source/WebKit/UIProcess/WebPageProxy.cpp
index a36d0ca506c9..fd0ead8b1a10 100644
--- a/Source/WebKit/UIProcess/WebPageProxy.cpp
+++ b/Source/WebKit/UIProcess/WebPageProxy.cpp
@@ -4674,7 +4674,7 @@ void WebPageProxy::receivedNavigationActionPolicyDecision(WebProcessProxy& proce
     if (preferences().loadWebArchiveWithEphemeralStorageEnabled() && navigation && policyAction == PolicyAction::Use && navigationAction->navigationType() != NavigationType::Reload && !isPolicyDataStore) {
         bool isSubstituteWebArchive = navigation->substituteData() && MIMETypeRegistry::isWebArchiveMIMEType(navigation->substituteData()->MIMEType);
         auto webarchiveURL = isSubstituteWebArchive ? URL { navigation->substituteData()->baseURL } : navigation->currentRequest().url();
-        if (isSubstituteWebArchive || (webarchiveURL.protocolIsFile() && webarchiveURL.string().endsWith(".webarchive"_s))) {
+        if (isSubstituteWebArchive || (webarchiveURL.protocolIsFile() && webarchiveURL.fileSystemPath().endsWith(".webarchive"_s))) {
             WEBPAGEPROXY_RELEASE_LOG(Loading, "receivedNavigationActionPolicyDecision: Swapping in non-persistent websiteDataStore for web archive.");
             if (navigation->targetItem() && navigation->targetItem()->dataStoreForWebArchive())
                 websiteDataStore = *navigation->targetItem()->dataStoreForWebArchive();

Background

Advisory: Apple Security Advisory

Attribute Value
CVE CVE-2024-44212
Bugzilla 279226
Component WebKit
Bug Class CrossOrigin
Severity medium
Commit 11494e6772915212…
Advisory Apple Advisory

Terminology

  • Web archive (.webarchive) — A single-file snapshot of a page and its subresources that WebKit can load, with its own baseURL origin.
  • Ephemeral / non-persistent data store — A website data store with no on-disk persistence, swapped in for web archive loads to isolate their cookies and storage.
  • URL string vs filesystem path — url.string() is the full serialized URL (scheme, path, query, fragment, encoding); url.fileSystemPath() is the decoded local path only.
  • Navigation policy decision — The UIProcess step that decides how a navigation proceeds, including which data store backs it.

Root Cause Analysis

WebPageProxy::receivedNavigationActionPolicyDecision decides whether a navigation should be loaded in a swapped-in non-persistent (ephemeral) website data store, which is done for web archives so their content is isolated. The webarchive detection tested webarchiveURL.string().endsWith(".webarchive") — i.e. it matched the suffix against the full URL string, which includes the query and fragment and percent-encoding, not just the file path. A file URL for a genuine .webarchive that carries a query or fragment (e.g. file:///path/x.webarchive?a or ...x.webarchive#a) has a .string() that does NOT end in .webarchive, so the archive was not recognized and the ephemeral-store swap was skipped; the archive then loaded in the normal persistent data store, so cookies associated with the archive’s origin were read from / written to the shared persistent store and could reach another origin. The fix matches against webarchiveURL.fileSystemPath(), the decoded filesystem path with query/fragment stripped, so the .webarchive classification reflects the real file rather than an attacker-manipulable URL string, and the ephemeral-store isolation is applied to exactly the webarchive loads it is meant for. The restored invariant is that ‘is this a .webarchive load’ is decided from the file path, not from a URL string that can be altered without changing the file. This runs in the UIProcess navigation policy path, not the sandboxed WebContent.

Attack Path

1. Host a web archive

Get the victim to open a .webarchive file (local file load or substitute data) that should be isolated in an ephemeral data store.

2. Perturb the URL string

Reference the archive with a URL whose string does not end in ‘.webarchive’ though the file path does — e.g. append a query or fragment (x.webarchive?a / x.webarchive#a).

3. Defeat archive detection

endsWith on url.string() fails to match, so receivedNavigationActionPolicyDecision does not swap in the non-persistent store.

4. Leak cookies across origins

The archive loads in the persistent store, so cookies tied to the archive’s baseURL origin are read/written in the shared store and can be sent to another origin.

Impact Assessment

No memory-safety primitive; this is a privacy/isolation logic bug. Mis-detecting web archives caused the ephemeral-store isolation to be skipped, so cookies belonging to one origin could be sent to another origin. It affects data confidentiality/integrity of cookies and executes in the UIProcess navigation path; there is no path to code execution. Rated medium.

Audit Directions

Other suffix/extension checks on url.string()

grep WebKit/WebCore for .string().endsWith( / .startsWith( / contains( on URLs used for security decisions; prefer fileSystemPath()/path() where a file is meant.

Data-store / storage-partition selection

Audit other branches choosing persistent vs ephemeral stores or storage partitions based on URL shape rather than resolved origin.

webarchive handling

Review all isWebArchiveMIMEType / .webarchive handling for consistent path-based classification across load, save, and policy paths.

Changed Functions

Function File Change Note
WebPageProxy::receivedNavigationActionPolicyDecision Source/WebKit/UIProcess/WebPageProxy.cpp modified Detects a web archive with webarchiveURL.fileSystemPath().endsWith(".webarchive") instead of webarchiveURL.string().endsWith(…), so query/fragment/encoding can’t hide or fake the archive suffix and ephemeral-store isolation is applied correctly.