← All issues

[2] Cross-Process Page Identity Confusion in didPostMessage

Under Site Isolation, a compromised renderer could borrow any tab's identifier and have the UI process deliver scripted postMessage payloads to it.

Severity: High | Component: WebKit UIProcess | 1ad0d2a

diff에 드러난 UI-process IPC는 전송자 소유 여부를 확인하지 않은 채 전역에서 WebPageProxyIdentifier를 조회하고 대상 페이지에 user-script 메시지를 전달합니다. 손상된 renderer는 이를 이용해 다른 renderer의 WKScriptMessageHandler에 payload를 주입할 수 있습니다. 이러한 이유로 High severity로 평가됩니다.

WebProcessProxy::didPostMessage()는 전역 WebPageProxy::fromIdentifier()를 통해 WebPageProxyIdentifier를 조회했습니다. 그러나 해당 페이지가 전송 측 WebProcess에 속하는지는 확인하지 않은 채 결과를 처리했습니다. site isolation 환경에서 이 identifier는 process에 독립적인 핸들입니다. 손상된 renderer가 임의 페이지의 identifier를 제공하면, 조작된 postMessage payload가 해당 페이지에서 전송된 것처럼 전달될 수 있습니다.

Source/WebKit/UIProcess/WebProcessProxy.cpp

RefPtr page = WebPageProxy::fromIdentifier(pageID);
if (!page)
return completionHandler(makeUnexpected(String()));
+ MESSAGE_CHECK_COMPLETION(isAssociatedWithPage(pageID), completionHandler(makeUnexpected(String())));
RefPtr controller = WebUserContentControllerProxy::get(identifier);
+ for (Ref remotePage : m_remotePages) {
+ if (remotePage->page() && remotePage->page()->identifier() == pageID)
+ return true;
+ }
+ if (m_pagesPendingClose.contains(pageID))
+ return true;

전역 조회 직후에 MESSAGE_CHECK_COMPLETION(isAssociatedWithPage(pageID), ...)가 추가되었습니다. isAssociatedWithPagem_remotePages(site isolation 환경의 cross-origin iframe 프로세스)와 새로 도입된 m_pagesPendingClose counted set을 함께 참조하도록 확장되었습니다. m_pagesPendingClose는 모든 Messages::WebPage::Close 전송을 래핑하는 새 헬퍼 sendPageCloseMessage가 채웁니다. ProvisionalPageProxy, RemotePageProxy, SuspendedPageProxy, WebPageProxy의 호출 지점은 이 헬퍼를 사용하도록 변경되었습니다.

전역 레지스트리로 cross-process 객체 핸들을 조회하는 UI-process IPC 핸들러에서 전송 프로세스 소유 여부 확인 누락.

WebPageProxy는 논리적인 탭을 나타내는 UI-process 객체입니다. site isolation 환경에서는 하나의 페이지에 여러 WebContent process가 연결될 수 있습니다. main-frame process 외에도 cross-origin iframe을 위한 per-site RemotePageProxy가 함께 붙습니다. WebProcessProxy는 각 renderer가 호스팅하는 페이지를 m_pageMap, m_provisionalPages, m_remotePages, m_suspendedPages를 통해 추적합니다. WebPageProxyIdentifier는 요청자에 상관없이 WebPageProxy::fromIdentifier()로 조회되는 전역 고유 핸들입니다. didPostMessage는 주입된 user script에서 window.webkit.messageHandlers.<name>.postMessage(...)를 호출할 때 UI-process가 처리하는 핸들러입니다. MESSAGE_CHECK는 실패 시 메시지를 중단하고 전송 측 WebContent process를 종료합니다.

이 핸들러는 renderer가 제공한 WebPageProxyIdentifier를 전역에서 조회했습니다. 소유권 확인은 수행하지 않았고, 조회된 페이지의 WebUserContentControllerProxy를 통해 payload를 그대로 전달했습니다. 손상된 WebContent process는 피해 페이지의 identifier와 직렬화된 JS payload를 담은 WebProcessProxy::DidPostMessage를 위조할 수 있습니다. UI process는 이를 해당 페이지에서 발생한 것처럼 처리하게 됩니다.

이번 수정은 이차적인 문제도 드러냅니다. isAssociatedWithPage는 두 가지를 새로 처리하도록 확장되어야 했습니다. 먼저 m_remotePages를 참조해, cross-origin iframe을 통해서만 페이지를 소유하는 process도 정상적으로 인식하도록 변경되었습니다. 한편 m_pagesPendingClose counted set도 새로 도입되었습니다. 이 set은 WebPage::Close round-trip이 진행되는 동안 isAssociatedWithPage가 true를 반환하도록 유지합니다. 기존 헬퍼가 처리하지 못했던 TOCTOU window를 이로써 닫게 됩니다.

Primitive: 공격자가 제어하는 postMessage payload를 피해 페이지의 WebUserContentControllerProxy에 cross-process로 주입하는 방식입니다. 애플리케이션 코드는 WKScriptMessageHandler callback을 자신의 문서에서 온 same-origin 입력으로 간주하는 것이 일반적입니다. 이 surface를 통해 navigation 트리거, 자격증명 관련 동작, 영구 상태 변경 등에 모두 도달할 수 있습니다.

이 취약점은 UI process가 WebContent process 사이에서 유지해야 할 per-process 경계를 약화시킵니다. 그 경계는 손상된 renderer threat model이 방어하려는 바로 그 경계에 해당합니다.