← All reports

[4] NetworkProcess missing IPC authorization on `WebPageProxyIdentifier`

MediumWebKit NetworkProcess IPC surfaceAuthBypass

A compromised renderer could borrow another WKWebView's cookie policy by name.

6ed0b1c

Medium — 이번 diff는 process 간 insecure direct object reference를 차단합니다. 다만 단독으로 성립하는 버그가 아니라 second-stage 버그입니다. 이미 장악된 WebContent process가 필요하고, 피해 page가 relaxed third-party cookie blocking을 opt-in한 상태여야 합니다. 두 조건이 모두 갖춰지지 않으면 identifier가 완화된 policy를 선택하지 못하고, 권한 확장도 발생하지 않습니다.

WebKit의 NetworkProcess는 IPC를 통해 각 WebContent process의 cookie 조회, resource load, WebSocket channel 생성을 처리합니다. 여기서 관련된 구성 요소는 두 가지입니다. 먼저 WebPageProxyIdentifier는 UIProcess가 생성하고 소유하지만, NetworkProcess에는 WebContent가 보낸 메시지에 담겨 전달됩니다. 다른 하나는 process 전역에서 page 단위로 관리되는 policy 집합으로, third-party cookie blocking이 완화된 page 목록을 기록합니다. 빠져 있던 invariant는 간단합니다. 하나의 connection은 자신이 실제로 권한을 가진 page만 지칭할 수 있어야 합니다.

관전 포인트: 장악된 WebContent process는 다른 page의 WebPageProxyIdentifier를 메시지에 넣을 수 있습니다. 다른 process에 속한 별도 WKWebView의 identifier도 예외가 아닙니다. 이 경우 해당 page의 완화된 third-party cookie policy가 공격자 자신의 요청에 적용되고, cross-origin cookie가 노출될 수 있습니다.

Commit message에 따르면, NetworkProcess는 renderer가 보낸 identifier를 신뢰할 수 있는 key로 그대로 사용했습니다. 송신 connection이 해당 page를 지칭할 권한이 있는지는 확인하지 않았습니다. 기존의 allowsFirstPartyForCookies(m_webProcessIdentifier, firstParty) check는 page가 아니라 registrable domain 단위로 동작합니다. 그래서 이번 수정에서는 process가 특정 page의 first-party cookie 접근 권한을 받는 시점에 해당 identifier도 함께 허용하도록 했습니다.

Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.cpp

+ if (!m_allowedWebPageProxyIdentifiers.contains(webPageProxyID))
+ return; // loader/socket 경로에서는 MESSAGE_CHECK에 준하는 처리

Source/WebKit/UIProcess/WebPageProxy.cpp

+ void WebPageProxy::addAllowedFirstPartyForCookies(..., WebPageProxyIdentifier pageID)
+ {
+ // identifier 허용을 first-party cookie 허용 과정에 통합
+ }

이번 변경으로 process별 allow-list인 m_allowedWebPageProxyIdentifiers가 추가되었습니다. 이 목록은 새로 추가된 WebPageProxy::addAllowedFirstPartyForCookies wrapper를 통해 채워집니다. 즉 process가 특정 page의 first-party cookie 접근 권한을 받을 때 identifier도 함께 등록됩니다. 기존의 navigation/process-swap 호출 지점은 모두 이 wrapper를 사용하도록 변경되었습니다. 이후 NetworkProcess는 renderer가 보낸 page identifier를 받아들이기 전에 validateCookieAccess, scheduleResourceLoad, performSynchronousLoad, createSocketChannel에서 allow-list를 확인합니다.

IPC trust boundary에서 client가 보낸 identifier를 권한 증표(capability)로 그대로 받아들이고, 송신 측이 이를 지칭할 권한이 있는지는 확인하지 않는 패턴.

4-process 모델. WebKit은 UIProcess, WebContent, Networking, GPU process로 나뉩니다. 이 중 WebContent는 신뢰할 수 없는 process입니다. 반면 NetworkProcess는 실제 요청을 전송하고 policy 상태를 보관합니다.

WebPageProxyIdentifier. UIProcess가 생성하고 소유하는 identifier입니다. 그러나 NetworkProcess로는 WebContent가 보내는 메시지(getRawCookies, cookiesForDOM, scheduleResourceLoad, createSocketChannel, ...)의 일반 필드로 전달됩니다.

Third-party cookie blocking 완화. _shouldRelaxThirdPartyCookieBlocking은 WKWebViewConfiguration의 opt-in 옵션입니다. 평소 NetworkProcess는 firstParty와 url이 cross-site이면 cookie 제공을 거부하는데, 이 옵션은 그 제한을 완화합니다. 이때 어떤 요청에 완화를 적용할지는 page identifier로 결정됩니다.

이 버그는 memory-safety 문제가 아닙니다. IPC trust boundary에서 authorization check가 누락된 경우로, insecure direct object reference의 process 간 버전에 해당합니다.

diff에서는 key 구조가 명확히 드러납니다. NetworkProcess::createNetworkConnectionToWebProcess는 m_pagesWithRelaxedThirdPartyCookieBlocking.addAll(parameters.pagesWithRelaxedThirdPartyCookieBlocking)을 호출합니다. 대상 컨테이너는 singleton NetworkProcess에 있는 Vector<WebPageProxyIdentifier>입니다. 즉 connection별로 분리되지 않고 모든 WebContent connection이 같은 상태를 공유합니다.

page identifier가 완화 여부를 결정한다는 점은 cookieRequestHeaderFieldValueDigest 위에 남아 있는 context line에서 확인됩니다: "The only caller never passed frame, page or web page proxy identifiers, so relaxed third-party cookie blocking never applied here." 문제는 이 identifier가 renderer로부터 전달되면서도 그 renderer와 대조된 적이 없었다는 점입니다. 따라서 장악된 WebContent process가 임의 page의 identifier를 넣으면, 완화된 policy가 자신의 요청에 적용될 가능성이 있었습니다. 다만 page 단위 완화 조회 방식, identifier 추측 가능성, loader/socket 경로에서의 귀속 오류는 예상 시나리오에 해당합니다. 실제로 page 간 정보 노출이 성립하려면 두 조건이 필요합니다. 피해 page가 _shouldRelaxThirdPartyCookieBlocking을 opt-in했어야 하고, 공격자가 해당 page의 identifier를 지칭할 수 있어야 합니다.

수정 방식이 page 단위 equality 비교가 아니라 process별 set인 데에는 이유가 있습니다. Site isolation 환경에서는 하나의 page가 정상적으로 여러 process에 걸쳐 존재합니다. main frame과 out-of-process subframe이 서로 다른 process에 있을 수 있기 때문입니다. 결과적으로 소유 관계는 다대다가 됩니다. identifier 허용을 first-party cookie 접근 권한 부여에 통합한 것도 같은 맥락입니다. 이렇게 하면 process가 이미 해당 page의 cookie 권한을 가진 경우에만 identifier를 지칭할 수 있게 됩니다.