← All issues

[2] WebPasteboardProxy missing frame-ancestry check under Site Isolation

Severity: High | Component: WebKit UI-process WebPasteboardProxy (Cocoa) | 69aa562

Rated High because the diff adds the missing subtree-membership check on attacker-supplied frame identifiers that a compromised WebContent process could name in a pasteboard IPC, where the pre-fix path could collect an unrelated cross-site frame's serialized DOM; escalation to a sandbox escape is not implied — the impact is a cross-site information disclosure gated on addressing the target FrameIdentifier and reading the pasteboard back.

WebPasteboardProxy performed no subtree checks on the remote frame IDs it was given, so it could cause content from an unrelated cross-site remote frame to be written to the pasteboard. The fix adds message checks ensuring every remote frame ID used within WebPasteboardProxy is a descendant of the root frame ID being copied.

Source/WebKit/UIProcess/Cocoa/WebPasteboardProxyCocoa.mm

+// Only allow exploring frames that descend from the provided root frame.
+static bool validateFrameIdentifiers(FrameIdentifier rootFrameIdentifier, const HashMap<FrameIdentifier, Ref<WebCore::LegacyWebArchive>>& localFrameArchives, const Vector<FrameIdentifier>& remoteFrameIdentifiers)
+{
+ auto isInSubtree = [&](WebFrameProxy& frame) {
+ for (RefPtr ancestor = &frame; ancestor; ancestor = ancestor->parentFrame()) {
+ if (ancestor->frameID() == rootFrameIdentifier)
+ return true;
+ }
+ return false;
+ };
+ auto isAllowed = [&](FrameIdentifier identifier) {
+ if (identifier == rootFrameIdentifier)
+ return true;
+ RefPtr frame = WebFrameProxy::webFrame(identifier);
+ return !frame || isInSubtree(*frame);
+ };
+ for (auto& [frameIdentifier, archive] : localFrameArchives) {
+ if (!isAllowed(frameIdentifier))
+ return false;
+ Ref protectedArchive = archive;
+ if (auto archiveFrameIdentifier = protectedArchive->frameIdentifier(); archiveFrameIdentifier && !isAllowed(*archiveFrameIdentifier))
+ return false;
+ for (auto subframeIdentifier : protectedArchive->subframeIdentifiers()) {
+ if (!isAllowed(subframeIdentifier))
+ return false;
+ }
+ }
+ for (auto identifier : remoteFrameIdentifiers) {
+ if (!isAllowed(identifier))
+ return false;
+ }
+ return true;
+}
+ // writeWebContentToPasteboard
+ MESSAGE_CHECK(validateFrameIdentifiers(*rootFrameIdentifier, content.localFrameArchives, content.remoteFrameIdentifiers), connection);
+ // writeWebArchiveToPasteBoard
+ MESSAGE_CHECK_COMPLETION(validateFrameIdentifiers(rootFrameIdentifier, localFrameArchives, remoteFrameIdentifiers), connection, completionHandler(WriteWebArchiveToPasteBoardResult::FailureDueToInvalidFrameIdentifiers, 0));

LayoutTests/http/tests/ipc/resources/write-web-archive-frame-ancestry-check-iframe.html

+ // Forge a WriteWebArchiveToPasteBoard IPC that names an out-of-process frame ID (the parent
+ // frame) as a remote subframe. This should fail with the FailureDueToInvalidFrameIdentifiers error.
+ let r = IPC.sendSyncMessage('UI', 0, IPC.messages.WebPasteboardProxy_WriteWebArchiveToPasteBoard.name, 1000, [
+ S(pbName), FrameID(myFrameID), u64(0n), u64(1n), FrameID(parentFrameID),
+ ]);

A new static helper validateFrameIdentifiers(rootFrameIdentifier, localFrameArchives, remoteFrameIdentifiers) walks each supplied frame identifier and confirms it is a subtree descendant of the root frame being copied. isAllowed() returns true for the root frame itself or — via WebFrameProxy::webFrame(identifier) and the isInSubtree lambda that walks parentFrame() up to the root — for any frame whose ancestry chain reaches rootFrameIdentifier; an unknown (null) frame is also allowed. The helper validates every key in content.localFrameArchives, each archive's own frameIdentifier() and subframeIdentifiers(), and every remoteFrameIdentifiers entry. writeWebContentToPasteboard gains a MESSAGE_CHECK, and writeWebArchiveToPasteBoard a MESSAGE_CHECK_COMPLETION. The WriteWebArchiveToPasteBoard reply type is widened from a bare int64_t changeCount to (WriteWebArchiveToPasteBoardResult result, int64_t changeCount) — a new enum class (Success / FailureDueToInvalidFrameIdentifiers / FailureOther) — so the WebProcess and test can observe rejection.

Missing frame-ancestry (subtree-membership) authorization on attacker-supplied frame identifiers crossing an IPC trust boundary in the Site Isolation pasteboard broker.

Site Isolation is WebKit's architecture that places cross-site iframes in separate WebContent processes, where each frame has a process-spanning FrameIdentifier used by the UI process to address frames. WebPasteboardProxy is a UI-process IPC message receiver that brokers pasteboard operations on behalf of WebContent processes. LegacyWebArchive is WebKit's serialized representation of a frame and its subframes (a .webarchive); createOneWebArchiveFromFrames assembles one archive from a root frame plus its local-frame archives and remote (out-of-process) subframe identifiers. WebFrameProxy::webFrame(identifier) resolves a FrameIdentifier to the UI-process proxy for that frame, whose parentFrame() chain expresses the frame tree.

MESSAGE_CHECK / MESSAGE_CHECK_COMPLETION are IPC validation macros that, on failure, treat the message as invalid (terminating the sender or returning an error) — the standard mechanism for enforcing invariants on attacker-controlled IPC input. The IPCTestingAPI is a test-only facility that lets layout tests synthesize raw IPC messages, used here to forge a request naming a non-descendant frame.

This is a missing authorization / cross-process capability check on an IPC handler — a Site Isolation boundary bypass. Before the fix, writeWebArchiveToPasteBoard and writeWebContentToPasteboard accepted a rootFrameIdentifier plus an arbitrary set of local-frame-archive keys and remoteFrameIdentifiers straight from an IPC message and passed them to createOneWebArchiveFromFrames without ever confirming those identifiers belonged to the subtree rooted at rootFrameIdentifier.

Under Site Isolation, cross-site subframes live in different WebContent processes but their FrameIdentifiers are globally addressable across the UI process. The UI process's WebPasteboardProxy is the trusted broker that assembles a multi-frame web archive, reaching into other frames' processes to collect their serialized DOM. With no ancestry validation, a WebContent process serializing a copy of its own frame could name a completely unrelated cross-site remote frame as a "remote subframe", causing that frame's content to be collected and written to the pasteboard.

🔒

Examines how a trusted UI-process broker handled untrusted frame identifiers across the Site Isolation boundary, and what cross-origin exposure the gap allowed.

Subscribe to read more

🔒

Multiple reusable audit directions for cross-process identifier validation under Site Isolation, with concrete IPC entry points to start from.

Subscribe to read more