[5] NetworkProcess null-deref on a BroadcastChannel message with a null name
A null channel name crashed the NetworkProcess while hashing the key — faulting before the hash table's own validator ever got to run.
diff가 수정하는 대상은 위조된 IPC 메시지로 유발되는 NetworkProcess의 null-StringImpl dereference입니다. fault 주소에 공격자가 제어하는 데이터는 포함되지 않으므로, 관찰되는 영향은 crash(가용성)에 한정됩니다. memory corruption으로 이어지는 경로는 존재하지 않으며, 이 취약점에 도달하려면 WebProcess가 이미 침해된 상태여야 합니다. 이러한 점들을 종합해 Low로 평가됩니다.
NetworkBroadcastChannelRegistry는 IPC로 전달받은 채널 이름을 HashMap<String, ...>의 key로 사용합니다. registerChannel()에서는 ensure()를 통해, unregisterChannel()과 postMessage()에서는 find()를 통해 각각 조회합니다. 침해되었거나 비정상적인 WebProcess는 name으로 null String을 전송할 수 있습니다. null String을 key로 조회하면 hashing 과정에서 null StringImpl이 역참조됩니다. StringHash::hash()는 key.impl()->hash()를 호출하는데, null String의 경우 impl()이 nullptr를 반환하기 때문입니다. 결과적으로 HashTable::validateKey()가 실행되기 전에 network process가 crash합니다. 이번 fix는 세 endpoint 모두에 MESSAGE_CHECK를 추가하여 null name을 거부하며, 기존의 origin 검증과 동일한 방식으로 처리합니다.
Source/WebKit/NetworkProcess/NetworkBroadcastChannelRegistry.cpp
LayoutTests/ipc/coreipc.js
Patch Details
registerChannel()과 unregisterChannel()에는 MESSAGE_CHECK(!name.isNull(), connection)이 추가되었습니다. postMessage()에는 MESSAGE_CHECK_COMPLETION(!name.isNull(), connection, completionHandler())가 추가되었습니다. 두 경우 모두 기존의 isValidClientOrigin(origin) 검증 직후, String name이 HashMap key로 사용되기 전에 위치합니다. 나머지 diff는 부수적인 변경입니다. IPC testing API를 통해 버그를 재현하는 새 layout test와, null String 인자를 직렬화할 수 있도록 coreipc.js의 ArgumentSerializer에 추가된 한 줄 변경이 포함됩니다.
Hash-map key로 사용되기 전, 공격자 제어 IPC 입력에 대한 null 검증 누락 — hashing 과정에서 backing StringImpl 역참조 발생.
Background
BroadcastChannel은 동일 origin의 browsing context 간에 메시지를 주고받을 수 있는 Web API입니다. NetworkProcess는 WebProcess 간 메시지 라우팅을 위해 중앙 registry를 관리합니다. MESSAGE_CHECK는 WebKit IPC 매크로로, 수신 메시지에 대한 조건을 검증합니다. 검증 실패 시 처리를 계속하지 않고 해당 connection을 종료합니다. WTF String은 refcount 기반의 StringImpl을 감싸는 wrapper입니다. null String의 경우 impl()은 null을 반환하며, StringHash::hash()는 key.impl()->hash()를 호출하여 key의 hash를 계산합니다. HashMap의 삽입 및 조회 시에는 먼저 key를 hashing한 뒤 HashTable::validateKey()를 실행합니다. IPCTestingAPI는 테스트 전용 기능으로, layout test에서 대상 process로 raw IPC 메시지를 직접 합성할 수 있습니다. 여기서는 침해된 WebProcess가 전송할 수 있는 메시지를 재현하는 데 활용되었습니다.
Analysis
검증되지 않은 IPC 입력으로 인한 null pointer dereference입니다. fix 이전에는 NetworkBroadcastChannelRegistry가 IPC로 전달된 채널 name을 null 여부 확인 없이 그대로 HashMap<String, ...> key로 사용했습니다. 각 endpoint는 isValidClientOrigin을 통해 ClientOrigin을 검증했지만, name에 대해서는 동일한 검증을 수행하지 않았습니다.
null String이 key로 사용되면, hash table은 HashTable::validateKey()에 도달하기 전에 key의 hash를 먼저 계산합니다. 이 과정에서 StringHash::hash()가 key.impl()->hash()를 호출합니다. null String의 경우 impl()은 nullptr를 반환하므로, null StringImpl이 역참조됩니다.
동작 방식을 정리하면 다음과 같습니다. m_broadcastChannels.ensure(origin, ...)는 정상적으로 통과됩니다. 이후 channelsForOrigin.ensure(name, ...) (register의 경우) 또는 .find(name) (unregister/postMessage의 경우)에서 null key를 hashing하려다 fault가 발생합니다. 침해된 WebProcess(또는 테스트의 IPCTestingAPI)를 통해 공격자는 RegisterChannel/UnregisterChannel/PostMessage IPC를 전송합니다. 이때 ClientOrigin은 유효한 값으로, name은 null로 설정합니다. NetworkProcess는 origin 검증을 통과한 뒤 null name을 hashing하려다 crash합니다. fault 주소에 공격자가 제어하는 데이터는 없으므로, 별도의 primitive 없이는 crash 이상으로 확장될 가능성은 없습니다.
이 취약점은 비정상적인 입력에 대한 WebProcess→NetworkProcess IPC trust boundary의 견고함을 약화시킵니다. NetworkProcess는 등록되는 BroadcastChannel name이 non-null String이라고 가정했지만, 이를 강제하지는 않았습니다. 침해된 WebProcess가 null name을 전송하면 이 가정이 깨집니다. 성공적으로 유발되면 공유 NetworkProcess가 crash하여 모든 browsing context의 네트워크 연결이 종료됩니다. 가용성에 영향을 미치는 denial of service에 해당합니다. memory safety나 origin 경계를 넘는 공격으로는 이어지지 않습니다.
fault 발생 지점이 미묘한 이유가 있습니다. crash는 HashTable::validateKey()가 실행되기 전인 hash 계산(StringImpl::hash) 과정에서 일어납니다. 따라서 hash table 내부의 방어적 검증은 모두 우회됩니다. 공격자가 제어하는 String이 HashMap key로 사용될 때는 반드시 IPC 경계에서 null/empty 검증을 수행해야 합니다. 이 책임을 container에 위임해서는 안 됩니다.
Note: WTF hashing의 정확한 동작 방식(StringHash::hash()가 validateKey() 전에 null StringImpl로 인해 fault를 일으키는 과정)과, BroadcastChannel이 NetworkProcess를 통해 cross-process로 라우팅되는 구조는 diff에 완전히 드러나지 않습니다. 이 부분은 WTF/WebKit 내부 구현에 의존합니다. 다만 null 검증의 누락과 그 위치(hash 기반 조회 이전)는 patch를 통해 직접 확인됩니다.
Audit directions
- struct의 한 필드는 검증하면서 sibling 필드를 null 검증 없이 HashMap key로 사용하는 IPC handler. IPC로 전달된
String을 map key로 사용하는 다른 NetworkProcess registry를 살펴볼 필요가 있습니다. ServiceWorker, storage, cache name map에서 origin은 검증하지만 name/key는 검증하지 않는 handler가 있는지 확인하십시오.Source/WebKit/NetworkProcess에서.ensure(와.find(호출을 검색하여, key가 IPC로 전달된const String&인 경우 앞에MESSAGE_CHECK(!name.isNull(), ...)가 위치하는지 확인하십시오. - null
String/StringImpl이 HashMap key로 사용되면HashTable::validateKey()실행 전에StringHash::hash()에서 crash가 발생합니다. WebKit 전반에 걸쳐 container 수준의 key 검증을 IPC 입력의 보안 경계로 의존하는 코드가 있는지 확인하십시오.validateKey관련 가정을 검색하고, IPC 진입점이 직접 null key를 거부하는지 확인하십시오. - 관련 endpoint 간 검증 대칭성. 하나의 IPC 메서드(예:
registerChannel)에 guard가 추가되면, 동일 key를 사용하는 모든 메서드(unregisterChannel,postMessage,removeConnection스타일의 정리 경로 등)가 동일한 불변 조건을 강제하는지 확인하십시오. 하나의 guard 하에 채워진 map이 다른 guard 하에서 조회될 수 있기 때문입니다.
mainThreadInitialize 경로를 거쳐 main/loader thread로 전달되며, 이 과정에서 실제 ThreadableWebSocketChannel을 소유하는 main-thread Peer가 생성됩니다. thirdPartyCookieBlockingDecisionForRequest와 shouldBlockCookies는 NetworkProcess에서 요청에 cookie를 포함할지 결정하는 두 루틴으로, 전자는 worker-origination signal을 입력으로 받아 worker fetch와 동일한 policy를 적용합니다.
Analysis
이 취약점은 logic error이자 privacy-policy bypass에 해당합니다. ITP third-party cookie blocking의 격차이며, memory safety 문제와는 무관합니다. 패치 이전에는 DedicatedWorker에서 시작된 WebSocket 연결 경로가 요청이 worker에서 시작되었다는 사실을 network layer까지 전달하지 않았습니다. Cocoa WebSocketTask constructor는 단순히 shouldBlockCookies()를 호출해 third-party cookie 처리를 결정했는데, 동등한 fetch 경로가 이미 갖추고 있던 worker context가 이 호출에는 빠져 있었습니다.
ITP의 third-party cookie 차단 결정은 요청이 first-party인지 third-party 컨텍스트인지, 그리고 worker에서 시작되었는지에 따라 달라집니다. worker signal이 없으면 DedicatedWorker 내에서 cross-origin endpoint로 전송되는 WebSocket handshake가 third-party cookie를 차단하지 않는 policy로 평가됩니다. 결과적으로 handshake에 policy가 제거해야 할 cookie가 그대로 포함되었습니다. 추가된 layout test는 정확히 이 상황을 재현합니다. localhost/127.0.0.1에 first-party cookie를 설정하고, worker에서 cross-origin WebSocket을 열어 cookie가 포함되지 않아야 함을 검증하는 방식입니다. 공격 시나리오를 살펴보면, attacker가 삽입한 페이지에서 대상 origin에 first-party cookie를 설정합니다. 이후 DedicatedWorker에서 해당 origin으로 cross-origin WebSocket을 엽니다. third-party cookie blocking이 활성화된 상태에서도 패치 이전에는 handshake에 대상의 cookie가 포함된 채 cross-origin endpoint로 전달되었습니다. 이 endpoint는 전달된 cookie를 기록해 cross-site 간 identity 연계에 활용하는 것이 가능합니다.
이 취약점은 memory safety가 아니라 ITP third-party cookie blocking이 보장하는 privacy 경계를 약화시킵니다. 영향을 받은 전제는, third-party cookie blocking이 활성화되면 페이지나 해당 worker에서 시작된 cross-origin handshake에 third-party cookie가 포함되지 않아야 한다는 것입니다. 패치 이전에는 DedicatedWorker fetch에 대해서는 이 전제가 성립했지만, DedicatedWorker WebSocket handshake에서는 위반되어 ITP가 방지하려는 cross-site tracking 및 identity 연계가 가능했습니다. code execution이나 cross-origin script 접근으로는 이어지지 않습니다.
WebKit에서 privacy 및 security policy는 병렬 요청 경로(fetch vs WebSocket vs WebTransport vs EventSource, 그리고 document vs worker vs service-worker 출처)를 따라 독립적으로 도출되는 경우가 많습니다. 한 경로에 새로운 컨텍스트 signal이 도입되더라도, 동일 계열의 다른 경로는 각각 개별적으로 연결될 때까지 기존의 약한 결정을 그대로 유지합니다.
Note: WebSocketTaskCocoa.mm의 핵심 변경사항, NetworkResourceLoadParameters를 통한 기존 fetch 전달 방식, 그리고 thirdPartyCookieBlockingDecisionForRequest가 적용하는 policy는 commit 메시지에 근거합니다. 표시된 diff는 plumbing 과정을 보여주며, cookie blocking 동작의 실질적 변경은 잘린 Cocoa hunk에 있습니다. 격차의 존재와 수정 방식은 patch 전반에 걸쳐 일관되게 뒷받침됩니다.
Audit directions
- 하나의 요청 경로(fetch)에서는 전달되지만 동일 계열 경로(WebSocket/WebTransport/EventSource)에서는 누락되는 privacy/security signal. 동일한 worker-origination 격차가 있는지 장기 연결 초기화 경로를 점검해야 합니다.
DedicatedWorkerGlobalScope에서의WebTransport및EventSource설정이isInitiatedByDedicatedWorker에 해당하는 signal을 NetworkProcess의 cookie 결정 과정까지 전달하는지 확인합니다.SocketProvider::initializeWebTransportSession과 EventSource loader 경로를 시작점으로, 각각의 cookie-policy 호출 지점을thirdPartyCookieBlockingDecisionForRequest와 비교합니다. - NetworkProcess 내
shouldBlockCookies(잔존 호출 지점. 각 지점을thirdPartyCookieBlockingDecisionForRequest(와 비교해 worker-origination 입력이 여전히 빠진 결정 위치를 파악합니다. 각 protocol의 task constructor(Cocoa/Curl/SoupcreateWebSocketTask및 이에 상응하는 WebTransport task)가 동일한 provenance flag를 수신하고 반영하는지 확인합니다. - 요청 출처 정보를 잃어버리는 cross-thread marshalling. WebCore worker 서브시스템의
postTaskToLoader/mainThreadInitialize방식 hop을 점검합니다. worker thread에서is<DedicatedWorkerGlobalScope>를 통해 산출된 origin/worker-type 컨텍스트가 cross-thread lambda에 캡처되는지, 혹은 scope type을 사용할 수 없는 main thread에서 재도출을 시도하는지 확인해야 합니다.WorkerThreadableWebSocketChannel::Bridge와Modules/내 유사한 Bridge 클래스를 시작점으로 삼습니다.