[1] WebAuthn UIProcess origin spoofing via unvalidated IPC fields
Severity: High | Component: WebKit UIProcess WebAuthentication | 315ac30
Rated High because the diff removes a missing-validation gap at the WebContent-to-UIProcess boundary that let a compromised renderer bind a WebAuthn ceremony to an arbitrary relying-party origin; escalation to cross-site credential impersonation requires only a prior renderer compromise, which is the assumed threat model, though the bug yields no memory-corruption primitive.
A compromised WebContent process could spoof the securityOrigin in FrameInfoData or the parentOrigin parameter when sending WebAuthn MakeCredential/GetAssertion IPC messages to the UI process. This would let an attacker page impersonate a different origin (e.g. a bank) for credential creation or assertion. This patch adds MESSAGE_CHECKs to prevent that.
Source/WebKit/UIProcess/WebAuthentication/WebAuthenticatorCoordinatorProxy.cpp
-void WebAuthenticatorCoordinatorProxy::getAssertion(FrameIdentifier frameId, FrameInfoData&& frameInfo, ... std::optional<WebCore::SecurityOriginData> parentOrigin, RequestCompletionHandler&& handler)
+void WebAuthenticatorCoordinatorProxy::getAssertion(IPC::Connection& connection, FrameIdentifier frameId, FrameInfoData&& frameInfo, ... std::optional<WebCore::SecurityOriginData> parentOrigin, RequestCompletionHandler&& handler)
{
RefPtr webPageProxy = m_webPageProxy.get();
...
+ RefPtr frame = WebFrameProxy::webFrame(frameId);
+ if (!frame) {
+ RELEASE_LOG_ERROR(WebAuthn, "Frame not found for WebAuthn GetAssertion request");
+ return handler({ }, static_cast<AuthenticatorAttachment>(0), ExceptionData { ExceptionCode::InvalidStateError });
+ }
+ if (frame->url().protocolIsInHTTPFamily()) {
+ auto expectedOrigin = SecurityOriginData::fromURLWithoutStrictOpaqueness(frame->url());
+ MESSAGE_CHECK_COMPLETION_BASE(frameInfo.securityOrigin == expectedOrigin, connection,
+ handler({ }, static_cast<AuthenticatorAttachment>(0), ExceptionData { ExceptionCode::InvalidStateError }));
+ }
+ if (parentOrigin) {
+ bool foundMatchingAncestor = false;
+ bool hasHTTPAncestor = false;
+ for (RefPtr ancestor = frame->parentFrame(); ancestor; ancestor = ancestor->parentFrame()) {
+ if (!ancestor->url().protocolIsInHTTPFamily())
+ continue;
+ hasHTTPAncestor = true;
+ auto ancestorOrigin = SecurityOriginData::fromURLWithoutStrictOpaqueness(ancestor->url());
+ if (*parentOrigin == ancestorOrigin) { foundMatchingAncestor = true; break; }
+ }
+ if (hasHTTPAncestor)
+ MESSAGE_CHECK_COMPLETION_BASE(foundMatchingAncestor, connection, handler(...));
+ }
handleRequest({ ..., WTF::move(frameInfo), ..., parentOrigin }, WTF::move(handler));
}
LayoutTests/http/tests/ipc/web-authenticator-get-assertion-spoofed-origin-crash.html
+const spoofedOrigin = { data: { variantType: 'WebCore::SecurityOriginData::Tuple', variant: { protocol: 'https', host: 'evil.com', port: {} } } };
+CoreIPC.UI.WebAuthenticatorCoordinatorProxy.GetAssertion(IPC.webPageProxyID, { frameInfo: { ... securityOrigin: spoofedOrigin, topOrigin: spoofedOrigin, ... }, options: { ... }, mediation: 0, parentOrigin: {} });
Patch Details
Both makeCredential and getAssertion now take an IPC::Connection& as their first parameter so the receivers can issue connection-terminating checks. Each handler looks up the real frame via WebFrameProxy::webFrame(frameId), bails with InvalidStateError if none is found, and — when the frame URL is HTTP(S) — recomputes the expected origin with SecurityOriginData::fromURLWithoutStrictOpaqueness(frame->url()) and asserts frameInfo.securityOrigin == expectedOrigin via MESSAGE_CHECK_COMPLETION_BASE. getAssertion additionally walks frame->parentFrame() ancestors and, if any HTTP(S) ancestor exists, requires parentOrigin to equal one of those ancestor origins. Two IPC-testing layout tests send spoofed-origin requests and expect the WebContent process to be terminated.
Trusting an IPC-supplied security origin from the untrusted WebContent process without re-validating it against UI-process-owned frame state.
Background
WebKit splits work between a sandboxed WebContent (renderer) process and a privileged UI process; security-sensitive operations cross this boundary as IPC messages, and the UI process must treat every field as attacker-controlled. The MESSAGE_CHECK/MESSAGE_CHECK_COMPLETION_BASE macros terminate the offending IPC connection — killing the WebContent process — when their predicate fails, which is why the regression tests are named *-crash. FrameInfoData.securityOrigin is the renderer's claim about a frame's origin; WebFrameProxy::webFrame(frameId) returns the UI process's own record of that frame, whose url() is authoritative. WebAuthn MakeCredential/GetAssertion perform credential ceremonies scoped to a relying-party origin, and for cross-origin (iframe) assertions a parentOrigin describes the embedder, which must match an actual ancestor frame's origin.
Analysis
This is an origin-spoofing / authority-confusion bug, not memory corruption. Before the fix, the UI-process WebAuthn receivers consumed frameInfo.securityOrigin (and the parentOrigin argument) directly from the IPC message and propagated them into handleRequest/buildClientDataJson without checking them against the authoritative frame state. In WebKit's IPC trust model the renderer is untrusted: any field it serializes must be re-validated against UI-process-owned ground truth, and here that step was simply absent.
a Aaaaaaaaaaa Aaaaaaaaaa Aaaaaaa Aaaaaa a Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaa Aaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaa Aa Aaaaaaaaa Aaaaaa Aaaaa Aaaaaaaaaaaaaaaaaaa Aaaaa Aaa Aaaa Aaaaa Aa Aaaaaa Aaaaaaaaaa Aaaaaaaa Aaa Aa Aaaaaaa Aaaaaaaa Aaaa Aaaaaa Aa Aaa Aaaaaaaa Aaaaaaaa a Aaaaaaaaa Aaa Aaaaaaaaaaaaaaaa Aaaaaa Aaaaa Aaa Aaa Aaaaaaaaaaaaa Aaaaaaa a Aaaaa Aa Aaaaaaaaa Aaaaa Aaa Aaaaaaa Aaaaaaa Aaa Aaaaaaaaaaaa Aaaaaaa Aaaaaaaaaaaa Aaaaaa Aaaaaaaaaaaaaa Aa Aaaaa Aa Aaaaaaaaa Aaaaaaaaa Aaaa Aa Aaaaaaaaaaa Aa a Aaaaaaaa Aaaaaaaaaaaaaaa Aaaaaa Aa a Aaaaaaaa Aaaa Aaa Aaaaaaa Aaaa Aaaa Aa Aa Aaa a Aaaaaaaaaaaaaaaaa Aaaaaaaaa Aaa Aaaaaaaa Aaaaa Aaaaaaaa Aaaaaaaaaaa Aaa Aaaaaaaaaa Aaaaaaaaaaa Aa Aaa Aaaaaaa Aaaaaa Aaaa Aaaaaaaaaaaaaaaaaaaaaaa Aaaaaa Aaaaa Aa Aaaaaaaaaa Aaaa Aaa Aaaaaaaa Aaaaaaa Aaaaaa Aaa Aaaa Aaaaaa Aa Aaa Aaaaaaaaaaaaa Aaaaaaaa Aa Aaaaaaaa Aaaa Aaa Aaaaaaaaaaaaaaa Aaaaaaaa Aaa Aaaaa
Aaaa Aaaaaaaaaaaaa Aaaaaaa Aaa Aaaaaaaaaaaaaaaaaaaaaaa Aaaaa Aaaaaaaa Aa Aa Aaaaaaa Aa Aaaaaaaa Aaaaaa Aaaaaaaa Aaaaaaaaaa Aaaaaa Aaaaa Aaaaaaaaaaa a Aaaaaaaa Aaaaaaaaaa a Aaaaa Aa Aaa Aaaaaaaaaaaaa Aaaaaa Aaaaaaaaa Aa Aaa Aaaaaaaaaaaaa Aaaaa Aaa Aaaa Aaaaaa Aa Aaa Aaaaaaaaaa Aaaaaa Aa Aaaaaaaa Aaa Aaa Aaaaaaaaaaa Aaa Aaaaaaaa Aaaaa Aaaaaaa Aaaaaaaa Aa Aaaaaaaaa Aa Aaaaaaaaaaa Aaaaa Aa Aaaaaaaaa Aaaaaa Aaaa Aa a Aaaaa Aaaaaaaaaaaaa Aaaa Aaaa Aa Aaa Aaaaaaaa Aaaaaaaaaaaaaa Aaa Aaa Aaaaaaaaaa Aaa Aaaaaa Aaaa Aaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaa Aaaaa Aaa Aaaaaaaaaaaaaaaa Aaaaaaa Aaa Aaaaaaaaaa Aaaaaa Aaaaaaaaa Aaa Aaaaaaaaaa Aaaaaa Aaa Aaaaaaaaaaaa Aaaaaaaaa Aaaaaaa Aaaaaaaa Aaaaaa Aaaaaaaa
🔒Examines how a renderer-supplied identity field crosses into a privileged process and what an attacker can claim before the boundary is enforced.
Subscribe to read more
Audit directions
a Aaaaaaaaaaaaaaaaaaa Aaaaaaa Aaaaaaa Aa a Aaaaaaaaaa Aaaaaaaaa Aaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaa Aaa Aaaaaaaaa Aaaa Aaaaaaa a Aaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaa Aaa Aa Aa Aaaaaaa Aaaaaaaaaaa Aaa Aaaaaa Aaaa Aaaaaaaaaaaaaaaa Aaaaa Aaaaaa Aaaaa Aaaaa Aaaaaaaaa Aa Aaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaa Aaaaaaaaaaaaaaaaa Aa Aaaaaaaaaaaaaaaaaaaa Aaaaaaaaaa Aaa Aaaaa Aaaaaaa Aaaa Aaaaaaaaaaa Aaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaa Aaa Aaaaaaaaaaaaaaa Aaa Aaaaaaaaaaaaaa Aaaaaa Aaaaaaaaa Aaaaaaa Aaaaaaaa Aaa Aaaa Aaa Aaaaaaaa Aaaaaaaaaaaa Aaaaaaaaa Aa Aaaaaaaaaaa Aaaaaaa a Aaaaaaaaaaaaaaaa
a Aaaaaaaaaaaaaa Aaaaaaaa Aaaaaaaa Aaaaaaaaaaaa Aaaaaaaa Aaaaaa Aaaa Aaaaaaa Aaaaaaaaaaaaaaa Aaa Aaaaa a Aaaaaaaaaaaa Aaaa Aa Aaaaaaaaaa Aaaaaaaa Aaaaaaa Aaaaaa Aaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaa Aaaa Aaa Aaaaaaa Aaaaaaaaaaaaaa Aa Aaaaaaa Aaa Aaaaaaaa Aaaaaaaaaaaaaaaaaaaaaa Aaaa Aaaaaa Aa Aaaaaa Aa Aaaa Aaaaaa Aaaaaaaaaaa a Aaa Aaa Aaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaa Aaaaa Aaaaaaaa Aaaaaaa Aaaa Aaa Aaaa Aaaaa Aaaaaaaa
a Aaaaaaaaaaaa Aaaaaaa Aaaaaaaaaaaa Aaaaaa Aaa Aaaaaaaaaaaaaaaaa Aaa Aaaaa Aaaaaa Aaaaa Aaaaaa a Aaaa Aaaaaaaaaaaaaaaaaaaa a Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa a Aaaaaaaa Aaaa Aaaaaa Aaaaaaa Aaa Aaaaaaa Aaaaaa Aaaaa Aaaa Aaaa Aaa Aaaaaaa Aa Aaaa Aaaaaa Aaaaa Aaaa Aa Aaa Aaaaaaa Aaa Aaaaaa Aaaaaa a Aaaaa Aaaaaaaa Aa Aa Aaaaaa Aaaaaa Aaaaa
🔒Multiple reusable audit patterns identified for IPC origin-validation gaps, with concrete UIProcess starting points for variant discovery.
Subscribe to read more