[3] WebContent-supplied origin forwarded as authorization key at four UI-process IPC sites
Severity: Medium | Component: WebKit UIProcess permission and authorization plumbing | 5ca4d87
Medium으로 평가된 이유는, 이 diff가 네 곳의 UI process handler에서 손상된 WebContent process가 위조할 수 있는 origin/domain 값을 권한 결정의 key로 사용하는 패턴을 차단하기 때문입니다. cross-site permission/identity spoof(예: 피해자 사이트의 geolocation 권한 행사)를 막는 변경이지만, memory corruption은 아닙니다. 또한 공격자가 이미 위조된 WebContent IPC 메시지를 전송할 수 있는 상태를 전제로 하기 때문에, severity는 앞서 다룬 renderer UAF보다 낮게 설정되었습니다.
네 곳의 UI process IPC handler는 DispatchedFrom=WebContent 메시지에 포함된 origin/domain 값을 그대로 시스템 서비스에 per-site authorization key로 전달하고 있었습니다. UI process가 직접 보유한 상태와 비교하는 과정이 없었습니다. 손상된 WebContent process가 origin을 위조하면, CoreLocation / AppSSO / MarketplaceKit / geolocation policy decider가 다른 사이트의 권한 결정을 적용하는 상황이 발생할 수 있었습니다.
이번 변경에서는 각 값을 UI process가 직접 보유한 상태에서 다시 도출하도록 수정되었습니다. 활용되는 기준값은 WebFrameProxy::url(), WebFrameProxy::securityOrigin(), commit된 main-frame URL입니다. SOAuthorizationSession은 InitiatorOrigin을 mainFrame()->url()에서 도출하고, interceptMarketplaceKitNavigation은 top-origin URL을 page.mainFrame()->url()에서 도출합니다. requestGeolocationPermissionForFrame은 FrameInfoData::securityOrigin을 UI가 계산한 origin으로 덮어쓰고 frame에 대해 MESSAGE_CHECK를 수행합니다. startUpdatingWithProxy는 authorization token에 RegistrableDomain을 바인딩하고, WebContent가 전달한 domain이 이와 일치하는지 MESSAGE_CHECK로 검증합니다. UI process가 origin을 직접 확인할 수 없는 webarchive/opaque-document 로드에는 기존 동작이 유지됩니다.
Source/WebKit/UIProcess/WebPageProxy.cpp
void WebPageProxy::requestGeolocationPermissionForFrame(IPC::Connection& connection, GeolocationIdentifier geolocationID, FrameInfoData&& frameInfo)
{
+ Ref process = WebProcessProxy::fromConnection(connection);
RefPtr frame = WebFrameProxy::webFrame(frameInfo.frameID);
- if (!frame)
- return;
+ MESSAGE_CHECK(process, frame);
+
+ if (!frame->url().host().isEmpty())
+ frameInfo.securityOrigin = frame->securityOrigin()->data();
+
+ WebCore::RegistrableDomain mainFrameDomain;
+ if (RefPtr mainFrame = m_mainFrame.get(); mainFrame && !mainFrame->url().host().isEmpty())
+ mainFrameDomain = WebCore::RegistrableDomain { mainFrame->url() };
- auto request = protect(internals().geolocationPermissionRequestManager)->createRequest(geolocationID, protect(frame->process()));
+ auto request = protect(internals().geolocationPermissionRequestManager)->createRequest(geolocationID, protect(frame->process()), WTF::move(mainFrameDomain));
Source/WebKit/UIProcess/WebGeolocationManagerProxy.cpp
- auto isValidAuthorizationToken = protect(page->geolocationPermissionRequestManager())->isValidAuthorizationToken(authorizationToken);
- MESSAGE_CHECK(proxy.connection(), isValidAuthorizationToken);
+ auto authorizedDomain = protect(page->geolocationPermissionRequestManager())->registrableDomainForAuthorizationToken(authorizationToken);
+ MESSAGE_CHECK(proxy.connection(), !!authorizedDomain);
+ MESSAGE_CHECK(proxy.connection(), authorizedDomain->isEmpty() || *authorizedDomain == registrableDomain);
Source/WebKit/UIProcess/Cocoa/SOAuthorization/SOAuthorizationSession.mm
- if (RefPtr sourceOrigin = m_navigationAction->sourceFrame() ? m_navigationAction->sourceFrame()->securityOrigin().securityOrigin().ptr() : nullptr; sourceOrigin && !sourceOrigin->isOpaque())
- initiatorOrigin = sourceOrigin->toString();
- if (m_page->mainFrame()) {
- if (m_action == InitiatingAction::SubFrame)
- initiatorOrigin = WebCore::SecurityOrigin::create(m_page->mainFrame()->url())->toString();
+ if (RefPtr mainFrame = page ? page->mainFrame() : nullptr) {
+ Ref mainFrameOrigin = WebCore::SecurityOrigin::create(mainFrame->url());
+ if (m_action == InitiatingAction::SubFrame || !mainFrameOrigin->isOpaque())
+ initiatorOrigin = mainFrameOrigin->toString();
Tools/TestWebKitAPI/Tests/WebKit/WKWebView/PermissionsAPI.mm
+ const realBytes = enc.encode('localhost');
+ const evilBytes = enc.encode('evil.host');
+ // byte-replace real host with forged host in captured IPC and replay
+ EXPECT_WK_STREQ(host, "localhost"_s);
+ EXPECT_FALSE(host.contains("evil"_s));
Patch Details
geolocation handler에 MESSAGE_CHECK(process, frame)가 추가되었습니다. frame->url().host()가 비어 있지 않은 경우, frameInfo.securityOrigin을 frame->securityOrigin()->data()로 덮어씁니다. commit된 main-frame URL에서 RegistrableDomain을 도출하여 createRequest에 전달합니다.
geolocation token 저장소는 HashSet<String>에서 HashMap<String, RegistrableDomain>으로 변경되었습니다. 이로써 각 token이 바인딩된 domain을 함께 보유하게 되었으며, 해당 값은 registrableDomainForAuthorizationToken을 통해 조회할 수 있습니다. startUpdatingWithProxy에서는 WebContent가 전달한 registrableDomain이 token에 바인딩된 domain과 일치하는지 MESSAGE_CHECK로 검증합니다. 단, UI에서 도출한 domain이 비어 있는 경우에는 이 검증을 건너뜁니다.
SOAuthorizationSession은 initiatorOrigin을 항상 SecurityOrigin::create(mainFrame->url())에서 도출하며, non-opaque 여부 확인을 일관되게 적용합니다. interceptMarketplaceKitNavigation은 top-origin을 SecurityOriginData::fromURL(page.mainFrame()->url())로 계산합니다.
UI process에서 WebContent가 전달한 origin을 그대로 authorization key로 신뢰하고, process가 직접 보유한 frame 상태로부터 재도출하지 않은 패턴.
Background
WebKit은 브라우저를 샌드박스 처리된 WebContent process(웹 JavaScript를 실행하는 비신뢰 영역)와 UI process(신뢰 영역, 시스템 서비스와 통신)로 분리합니다. 양쪽은 IPC를 통해 통신하며, DispatchedFrom=WebContent로 태깅된 메시지는 비신뢰 프로세스에서 발원합니다. 해당 프로세스가 손상되면 메시지의 모든 필드는 공격자가 제어할 수 있는 값으로 간주해야 합니다. MESSAGE_CHECK는 IPC invariant를 검증하는 WebKit 매크로로, 검증 실패 시 송신 프로세스를 종료합니다. FrameInfoData::securityOrigin은 frame을 설명하는 여러 IPC 메시지 안에 직렬화되는 origin 기술자입니다. WebFrameProxy는 frame의 UI process 측 미러로, WebFrameProxy::securityOrigin()과 WebFrameProxy::url()은 WebContent가 전달하는 값과 무관하게 UI process가 navigation commit 시점에 직접 계산한 origin/URL을 반환합니다. RegistrableDomain은 site key로 사용되는 eTLD+1 단위 그룹입니다.
geolocation authorization token은 사용자가 특정 사이트를 승인할 때 UI process가 생성하는 UUID입니다. WebContent process는 이후 StartUpdating 시점에 이 token을 제출해 위치 정보 수신을 시작합니다. AppSSO/SOAuthorization(InitiatorOrigin)과 MarketplaceKit(top origin)도 마찬가지로 UI process에서 origin을 받아 요청 사이트의 신원으로서 시스템 서비스에 전달합니다. IPC Testing API(IPCTestingAPIEnabled)는 외부로 나가는 IPC 바이트를 캡처하고 재전송할 수 있는 테스트 도구로, 위조 메시지를 전송하는 손상된 WebContent를 모델링하는 데 활용됩니다.
Analysis
이 취약점은 memory corruption이 아니라 authorization key origin spoofing 및 confused-deputy 로직 결함에 해당합니다. 패치 이전에는 네 곳의 UI process handler가 DispatchedFrom=WebContent 메시지에 포함된 origin/domain 값을 UI process 자체 상태와 비교하지 않고, per-site 권한 결정의 authoritative key로 그대로 사용하고 있었습니다. WebContent process는 비신뢰 대상입니다. 해당 프로세스가 손상되면, 직렬화하는 모든 필드는 공격자가 제어할 수 있는 값으로 간주해야 합니다.
구체적으로, requestGeolocationPermissionForFrame은 FrameInfoData::securityOrigin을 embedder의 권한 UI에 그대로 전달했습니다. StartUpdating 검증에서는 token의 존재 여부만 확인했을 뿐, 요청 domain이 권한을 부여받은 domain과 일치하는지는 검증하지 않았습니다. SOAuthorizationSession은 AppSSO InitiatorOrigin을 sourceFrame()->securityOrigin()에서 도출했고, interceptMarketplaceKitNavigation은 NavigationRequester::topOrigin을 사용했습니다.
Aaaaaaaaaaa Aaa a Aaa Aaaa Aa Aaa Aaaaaaa Aaaaaa Aaaa Aaa Aaaaaaa Aaaa Aaaaaaa Aaaaaaaaaaaaaaaaa Aa Aaaaaaa Aaaaaa Aaaaa Aaa Aaaaaa Aaa Aaaaaa Aaa Aaaaaaaaaaaaaaaaaaaa Aa Aaaa Aaa Aaaa Aaaaaaa
Aaaaaaaaaa Aaaaa Aaa Aaaaaaa Aaaa Aa Aaa Aaaaaaaaa Aaaaaaa Aa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa a Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaa Aaa Aa Aa Aaaaaaaaaaaaaaaaaa Aaa Aaaaaaaaaaaaaaaaaa Aaa Aaa Aaa Aaaaa Aaaaaa
Aaaaaaaaaaa Aaaaaa Aaaa Aaa Aaaaaa Aaaaa Aaaa Aa Aaa Aaaaaaaaaaaaaaaaaaaa Aa Aaaaaaaaaaaaaaaa Aaaaaa Aa Aaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaa Aaaa Aaaaaa Aaa Aaaa Aaaa Aaa Aaaa Aaaa Aa Aaa Aaaa Aa Aaaaaaa Aaaaaaaaaaaaaaaaaaaaa Aaaaaaa Aaaaaaaaaaaaaaaa Aaaaaaa Aaaa Aaa Aaaa Aa Aaa Aaa Aaaaaaaa Aaa Aaa Aaaaaa
a Aaaa Aaaaaaaaaaaaa Aaaaaa Aaaaaaaaaa Aaaaaaa Aa Aaaaaaaa Aaaa Aaa Aa Aaa Aaaa Aaa Aaaaaaaaaaa Aaaaaaa Aaa Aaaa Aaa Aa Aaaa a Aaa Aaa Aaaaaaaaaa Aaaaaaaa Aa Aaa a Aaaaaa
Aaa Aaa Aaaaaaaaaa Aaa Aaaaa Aaaaaaaa Aa Aaa Aa Aaa Aaaa Aaaaaa Aaa Aaaaaaa Aaaaaaa Aaaa Aaaaa Aaaa Aaa Aaaaaaaaaaa Aaa Aaaaaa Aaaaaaaaa Aaaaaaa Aaaaaaaaaaaaaa Aaa Aaaaaaaaaa Aaaaaaaaaa Aaaaaaaa Aa Aaaa Aaaa Aaa Aaaa Aa Aaaa Aa Aaa Aaaa Aa a Aaaaaa Aaa Aaaa Aaa Aaaaaaaaaaa Aaa Aaaaaa Aaaaaaaaaaaaaaaaaaaaaa Aaa Aaaaaaa Aaaa Aaaaaa Aa Aaa Aaaaaaaaaa Aaa Aaaa Aaa a Aa Aaa Aaa Aa Aaaaaaaaaa Aaaaaaaaaaaaaaaaaaa Aaaaaa Aaaaaa
Aaa Aaa Aaaaaa a Aa Aaaa Aaaa Aaaaaaaa Aa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaa Aa Aa Aaaaaaa Aaaaaaaaa Aa Aaaaaaaaaaaaa Aaaa Aa Aaaa Aaa Aaaaaa
Aaaaaaaaaaaaaaaaaaaaaaaaaa Aa Aaaaaaa Aaa Aaaaaaa Aa Aa Aa a Aaa Aa Aaaa a Aaa Aaa Aaa Aa Aa Aaaaaa Aa Aaaaaaaa Aa Aaa a Aa Aaa Aa Aaaaaaaaaaa Aaaa Aa Aaa Aaa Aaaa Aa Aaaaaa
Aaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaa Aa Aaa Aa Aa Aaaaaaaaaaa Aaaaaaaaaaaaaaaaaa Aaa Aaa Aaaaaaaaaaaaaaaaaaaaa Aa Aaa Aaaaa Aa Aaaa Aaaa Aaaaaa Aaaa Aa Aaaaa Aaa Aaaaaa a Aa Aaaaa Aaaaaaaa Aaa Aaa Aaa Aaaaa Aaa Aaaaaaa Aa Aaaaaa
🔒The cross-process trust model behind these four handlers, and what a compromised renderer could actually claim, is examined in depth.
더 확인하려면 구독해 주세요
Audit directions
a Aaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaa Aaaaaaaaaaaaa Aa Aa a Aaaaaa Aaa Aaaaa Aa Aa Aaa Aaaa Aaaa Aa Aaaaaaa Aaa Aaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aa Aaaaaaaaaaa Aaa Aaaa Aaaa Aaaaaaaaa Aaaaaaaa Aa Aaaa Aaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaa Aaaaaaa Aaa Aaaaaaaaaaaaaaaaaa Aaaaaaaaaaa Aaa Aaaaa Aaa Aa Aa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaa Aaaaaa
a Aaaa Aa Aaaaaaaaaa Aa Aaaa Aa Aa Aaa Aaaa Aaaaaaaaaa Aaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaa Aa Aaa Aaaaaaaaaaaaaaaa Aaaaaa Aaa Aaa Aaaa Aaaaaaaaa Aaaaaaaaaaaaa Aaaaa Aaaa Aaaa Aaaa a Aaaaaa Aaaa Aaaaaaaaaaaaaaaaaaa Aaaa Aaaaaaaaaa Aaa Aaaaaa Aaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaa Aaaaaaaaaaaaaa Aaaaaaaaaaaaaa Aaaaaaaaaaaaaaa Aaaaaaaaa Aaaa Aa Aaa Aaaaaaaaaaaaa Aaa Aaaaaaaaaaaaaaaa Aaaaa Aaaaaa
a Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaa Aa Aa Aaa Aaaaaaaaaa Aa Aa Aaa Aaaa Aaaa Aaaa Aaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaa Aaaaa Aaaaaaaaaa Aaaaaaaa Aaaaa Aaaaaa Aaaaaaaaaaaaaaaaa Aaa Aaa Aaa Aaa Aaa Aa Aaaa a Aaa Aaaa Aaaa Aaaaaaaaaa a Aaaaaaaa Aaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaa Aaa Aaaaaa
a Aaaaaaaaaaaa Aaaaaaaaa Aaaaa Aa Aaa Aa Aaaaaaaaaaaaaaa Aaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaa Aaaaaaaaa Aaa Aaaaaaaa Aaaaa Aa Aaa Aa Aaa Aaaaaaaaaa Aa Aaaaaaaaaaaaaaaa Aaaaa Aaaaaa Aaaa Aaa Aaaaa Aaa Aaa Aaaaaaaaaa Aaaa a Aaa a Aa Aaaaaa Aaaaaaaaaaa Aaaaaaa Aaaaaaa Aaa Aaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaa
🔒Four reusable audit patterns identified, with concrete UIProcess starting points for finding sibling instances of this trust-boundary flaw.
더 확인하려면 구독해 주세요