← All issues

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

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

diff가 공격자 제공 frame identifier에 대한 subtree 소속 검증을 추가한다는 점에서 High로 평가됩니다. 패치 이전에는 손상된 WebContent process가 pasteboard IPC에 임의의 frame identifier를 지정할 수 있었고, 관련 없는 cross-site frame의 직렬화된 DOM이 수집되는 경로가 존재했습니다. 단, sandbox escape은 포함되지 않으며, 영향 범위는 대상 FrameIdentifier를 특정하고 pasteboard를 다시 읽어오는 조건 하의 cross-site 정보 유출에 한정됩니다.

WebPasteboardProxy는 수신한 remote frame ID에 대해 subtree 검증을 전혀 수행하지 않았습니다. 이로 인해 관련 없는 cross-site remote frame의 콘텐츠가 pasteboard에 기록될 수 있었습니다. 이번 수정에서는 WebPasteboardProxy 내에서 사용되는 모든 remote frame ID가 복사 대상 root frame ID의 하위 frame인지 확인하는 message check가 추가되었습니다.

Source/WebKit/UIProcess/Cocoa/WebPasteboardProxyCocoa.mm

+// 제공된 root frame에서 파생된 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

+ // 프로세스 외부의 frame ID(부모 frame)를 remote subframe으로 지정하는
+ // WriteWebArchiveToPasteBoard IPC를 위조합니다. FailureDueToInvalidFrameIdentifiers 오류로 실패해야 합니다.
+ let r = IPC.sendSyncMessage('UI', 0, IPC.messages.WebPasteboardProxy_WriteWebArchiveToPasteBoard.name, 1000, [
+ S(pbName), FrameID(myFrameID), u64(0n), u64(1n), FrameID(parentFrameID),
+ ]);

새로운 static helper validateFrameIdentifiers(rootFrameIdentifier, localFrameArchives, remoteFrameIdentifiers)는 전달된 각 frame identifier를 순회하며, 복사 대상 root frame의 subtree에 속하는지 확인합니다. isAllowed()는 root frame 자체에 대해서는 true를 반환합니다. 그 외의 frame은 WebFrameProxy::webFrame(identifier)로 조회한 뒤, isInSubtree lambda가 parentFrame()을 따라 root까지 거슬러 올라가 ancestry chain이 rootFrameIdentifier에 도달하는지 검증합니다. 알 수 없는(null) frame 역시 허용 대상으로 처리됩니다. 이 helper는 content.localFrameArchives의 모든 key를 검증하며, 각 archive 자체의 frameIdentifier()subframeIdentifiers(), 그리고 모든 remoteFrameIdentifiers 항목도 마찬가지로 확인합니다. writeWebContentToPasteboard에는 MESSAGE_CHECK가, writeWebArchiveToPasteBoard에는 MESSAGE_CHECK_COMPLETION이 추가되었습니다. WriteWebArchiveToPasteBoard의 응답 타입은 기존의 단순한 int64_t changeCount에서 (WriteWebArchiveToPasteBoardResult result, int64_t changeCount) 형태로 확장되었습니다. WriteWebArchiveToPasteBoardResult는 Success / FailureDueToInvalidFrameIdentifiers / FailureOther 값을 갖는 새로운 enum class로, WebProcess와 테스트 코드가 거부 결과를 확인할 수 있게 됩니다.

Site Isolation pasteboard broker의 IPC trust boundary를 넘는 공격자 제공 frame identifier에 대한 frame ancestry(subtree 소속) 검증 누락.

Site Isolation은 cross-site iframe을 별도의 WebContent process에 배치하는 WebKit의 아키텍처입니다. 각 frame은 프로세스 전반에 걸쳐 고유한 FrameIdentifier를 가지며, UI process는 이를 통해 frame을 식별합니다. WebPasteboardProxy는 WebContent process를 대신해 pasteboard 작업을 중개하는 UI-process IPC 메시지 수신자입니다. LegacyWebArchive는 frame과 하위 frame을 직렬화한 WebKit의 표현 형식(.webarchive)이며, createOneWebArchiveFromFrames는 root frame과 local frame archive, 그리고 remote(프로세스 외부) subframe identifier를 조합해 하나의 archive를 생성합니다. WebFrameProxy::webFrame(identifier)FrameIdentifier를 해당 frame의 UI-process proxy로 변환하며, parentFrame() 체인이 frame tree 구조를 나타냅니다.

MESSAGE_CHECKMESSAGE_CHECK_COMPLETION은 IPC 유효성 검사 매크로입니다. 검증 실패 시 해당 메시지를 유효하지 않은 것으로 처리하고, 발신자를 종료하거나 오류를 반환합니다. 공격자가 제어하는 IPC 입력에 대해 불변 조건을 강제하는 표준 메커니즘입니다. IPCTestingAPI는 레이아웃 테스트에서 원시 IPC 메시지를 직접 합성할 수 있게 해주는 테스트 전용 기능으로, 이번 테스트에서는 비하위 frame을 지정하는 요청을 위조하는 데 사용되었습니다.

이 취약점은 IPC handler에서 발생한 인가 누락이자 프로세스 간 capability 검증 부재로, Site Isolation 경계 우회에 해당합니다. 패치 이전에는 writeWebArchiveToPasteBoardwriteWebContentToPasteboard가 IPC 메시지에서 받은 rootFrameIdentifier, 임의의 local frame archive key, remoteFrameIdentifiers를 그대로 createOneWebArchiveFromFrames에 전달했습니다. 해당 identifier들이 rootFrameIdentifier를 root로 하는 subtree에 속하는지는 전혀 확인하지 않았습니다.

Site Isolation 하에서 cross-site subframe은 서로 다른 WebContent process에 존재하지만, FrameIdentifier는 UI process 전반에서 전역적으로 주소를 지정할 수 있습니다. UI process의 WebPasteboardProxy는 다른 frame의 프로세스에 접근해 직렬화된 DOM을 수집하고 multi-frame web archive를 조합하는 신뢰된 중개자입니다. ancestry 검증이 없는 상황에서는, 자신의 frame을 직렬화하는 WebContent process가 전혀 관련 없는 cross-site remote frame을 "remote subframe"으로 지정할 수 있었습니다. 결과적으로 해당 frame의 콘텐츠가 수집되어 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.

더 확인하려면 구독해 주세요

🔒

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

더 확인하려면 구독해 주세요