CVE-2025-43376
Overview
Background
- iCloud Private Relay
- Apple privacy feature that routes a user’s web traffic (and is expected to route DNS) through two relays so the local network and ISP cannot see the destinations being contacted.
- nw_resolver
- A CFNetwork/Network.framework object that performs hostname resolution according to given parameters; it can be created from an endpoint or from an already-evaluated network path.
- nw_path_evaluator / nw_path
- Network.framework machinery that evaluates how traffic to an endpoint should egress (interface, proxy, relay), producing an nw_path that encodes that routing decision.
- DNS prefetch
- A browser optimization (e.g. rel=dns-prefetch) that resolves hostnames ahead of use, meaning hostnames can be looked up automatically from page content.
- privacy_level_silent
- An nw_context privacy setting used here to suppress metadata/logging on the resolution, orthogonal to whether the query is actually routed through Private Relay.
Root Cause Analysis
DNSResolveQueueCFNet::performDNSLookup (Source/WebCore/platform/network/cf/DNSResolveQueueCFNet.cpp) performs WebCore’s asynchronous DNS prefetch/resolution using CFNetwork’s low-level nw_resolver API. Before the patch it configured the resolver’s parameters and privacy context (nw_context_set_privacy_level(…, nw_context_privacy_level_silent) and nw_parameters_set_context) and then created the resolver directly from the target endpoint via nw_resolver_create_with_endpoint(hostEndpoint, parameters). The invariant that must hold when iCloud Private Relay is enabled is that name resolution — like the connection itself — must be carried over the Private Relay proxy path rather than being sent as a plaintext query to the locally configured/default DNS servers. Creating a resolver directly from an endpoint apparently does not run the network path evaluation that decides how (and through which proxy/interface) traffic should egress, so the DNS query could be issued outside the Private Relay tunnel, exposing the hostnames being resolved to the local network / ISP even though Private Relay was on.
The fix inserts an explicit path-evaluation step: it creates an nw_path_evaluator for the endpoint with the same parameters (nw_path_create_evaluator_for_endpoint), copies the resulting nw_path (nw_path_evaluator_copy_path), and then builds the resolver from that path (nw_resolver_create_with_path(path)). Resolving over the evaluated path makes the query honor the Private Relay routing/proxy decision, restoring the invariant that DNS is tunneled with the rest of the connection. The companion change in Source/WebCore/PAL/pal/spi/cf/CFNetworkSPI.h merely declares the newly used SPI (the nw_path_evaluator object type and the three functions), which is a prerequisite for calling them and not itself a bug. That the pre-patch path leaked queries is grounded in the CVE description and the direction of the fix; the precise internal reason nw_resolver_create_with_endpoint skips proxy routing is an inference about CFNetwork behavior, not something the diff itself spells out.
Attack Path
- Victim relies on Private Relay for privacy A user browses with iCloud Private Relay enabled, expecting DNS as well as connection traffic to be hidden from the local network and ISP.
- Page triggers DNS resolution Any navigation or resource with hostnames — including link rel=dns-prefetch or many subresources on an attacker-controlled page — causes WebCore to call DNSResolveQueueCFNet::performDNSLookup for those hosts.
- Resolver created directly from endpoint Pre-patch, the resolver is built via nw_resolver_create_with_endpoint without path evaluation, so the query is emitted over the default network path instead of the Private Relay proxy.
- On-path observer captures queries A local-network or ISP-level observer (the attacker) sees the plaintext DNS queries, learning which hostnames the victim is visiting despite Private Relay being on.
- Deanonymization / tracking The leaked query stream lets the observer profile the victim’s browsing, defeating the privacy guarantee; there is no memory corruption and no code execution involved.
Impact Assessment
Changed Functions
| Function | Change | Notes |
|---|---|---|
DNSResolveQueueCFNet::performDNSLookupSource/WebCore/platform/network/cf/DNSResolveQueueCFNet.cpp |
modified | Replaced nw_resolver_create_with_endpoint(endpoint, parameters) with a path-evaluated flow: create nw_path_evaluator for the endpoint, copy its nw_path, then nw_resolver_create_with_path(path), so DNS honors Private Relay routing. |
CFNetworkSPI.h SPI declarationsSource/WebCore/PAL/pal/spi/cf/CFNetworkSPI.h |
modified | Added OS_OBJECT_DECL/typedef for nw_path_evaluator and declared nw_path_create_evaluator_for_endpoint, nw_path_evaluator_copy_path, and nw_resolver_create_with_path so the new resolver-creation path can be called. |
Files Changed
Source/WebCore/PAL/pal/spi/cf/CFNetworkSPI.hSource/WebCore/platform/network/cf/DNSResolveQueueCFNet.cpp
Audit Directions
- Other resolver/connection creations in the CFNet DNS pathWithin DNSResolveQueueCFNet.cpp and sibling CFNet networking code, grep for ’nw_resolver_create_with_endpoint’, ’nw_connection_create’, and any endpoint-first object creation that skips ’nw_path_create_evaluator’ — verify each privacy-sensitive lookup goes through an evaluated path when Private Relay/advanced privacy is active.
- Private Relay routing consistency across platformsCompare the CFNet resolver flow with other DNSResolveQueue backends and platform DNS/proxy code; grep for ‘privacy_level’, ‘PrivateRelay’, ‘HAVE(SYSTEM_SUPPORT_FOR_ADVANCED_PRIVACY_PROTECTIONS)’, and ’nw_parameters’ to find places that set a privacy context but never evaluate a proxy path.
- New SPI usage correctnessAudit every caller of nw_resolver_create_with_path / nw_path_evaluator_copy_path (grep those symbols) to ensure the evaluated path is non-null and actually reflects the intended endpoint+parameters, so a failed evaluation does not silently fall back to a leaking default resolution.