[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.
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
Patch Details
전역 조회 직후에 MESSAGE_CHECK_COMPLETION(isAssociatedWithPage(pageID), ...)가 추가되었습니다. isAssociatedWithPage는 m_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 핸들러에서 전송 프로세스 소유 여부 확인 누락.
Background
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를 종료합니다.
Analysis
이 핸들러는 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이 방어하려는 바로 그 경계에 해당합니다.
Audit directions
- 전역 레지스트리를 통해 cross-process 핸들을 조회하는 UI-process IPC 핸들러.
WebProcessProxy.cpp와WebProcessProxy.messages.in에서 모든WebProcessProxy::*IPC 핸들러를 점검합니다.WebPageProxyIdentifier,FrameIdentifier,WebUserContentControllerProxyidentifier 등을 인자로 받는 핸들러가 대상입니다. 각 핸들러에서 조회된 객체를 사용하기 전에MESSAGE_CHECK(isAssociatedWithPage(...))(또는 동등한 소유권 확인)가 존재하는지 확인합니다.Source/WebKit/UIProcess/*.cpp에서fromIdentifier(를 검색해, 결과가MESSAGE_CHECK없이 사용되는 경우를 찾습니다. - 오래된 헬퍼 조건식. 이번 수정에서
isAssociatedWithPage는m_remotePages와m_pagesPendingClose모두를 처리하도록 확장되어야 했습니다.WebProcessProxy에서WebPageProxyIdentifier를 키로 사용하는WeakHashSet/HashMap멤버를 모두 나열하고, 헬퍼 구현과 대조합니다. service-worker 페이지, prewarmed 페이지, PiP overlay 등은 조용히 누락될 가능성이 있습니다. - UIProcess 측 teardown과 renderer 응답 사이의 TOCTOU.
m_pagesPendingCloseset은WebPage::Close처리가 진행되는 동안isAssociatedWithPage가 true를 반환하도록 유지합니다. 같은 window가 존재하는지 다른 UI-process teardown 메시지도 점검합니다. 대상은WebFrameProxy제거,WebUserContentControllerProxy분리, drawing-area 소멸입니다. UI-process 상태가 지워진 뒤 비동기 응답이 도착하기 전까지 renderer가 합법적으로 전송할 수 있는 IPC가 해당됩니다. WebPageProxyIdentifier의 도달 가능성과 예측 가능성. 이 identifier는 손상된 renderer가 관찰하고 재전송할 수 있는 IPC payload에 포함됩니다. 할당 방식이 예측 가능한지, 즉 관찰하지 못한 identifier를 열거할 수 있는지 확인합니다. typedef와generate()구현부터 살펴보는 것이 시작점입니다.