Medium CVSS 6.5 webkit Bypass 🔧 Commit mapped

Overview

Medium
Severity
6.5
CVSS
No
Exploited ITW
Fixed
Fix Status
DescriptionA malicious website may cause unexpected cross-origin behavior
ComponentWebCore HTML
Bug ClassBypass
Tracker265812
Fix commit863558a77cbf (WebKit/WebKit) +270/-220
CWECWE-284
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:N
CISA KEVNot listed
CreditedJames Lee (@Windowsrcer)
Disclosed2024-01-22

Background

Permissions/Feature Policy
A mechanism that gates powerful features (camera, microphone, fullscreen, geolocation, payment) per embedding context, controlled by the embedder’s allow attribute or default rules.
Embedding elements
iframe, embed, object and frame can all host nested browsing contexts; policy must apply to all of them, not just iframe.
defaultPolicy
The feature policy applied when no allow attribute is present; the fix uses it for non-iframe owners.

Root Cause Analysis

This fixes a Permissions/Feature Policy enforcement gap: policy was applied only to iframe owners, not to other embedding elements. isFeaturePolicyAllowedByDocumentAndAllOwners walks from a document up to the top document and, for each ancestor, consults the owner element’s feature policy. Pre-patch it only handled the case where the owner was an HTMLIFrameElement: it did dynamicDowncast<HTMLIFrameElement>(ownerElement) and, only if that succeeded, checked iframe->featurePolicy().allows(type, origin); for any other embedding element (for example embed, object, or frame) the owner branch was skipped, so the powerful-feature gate (camera, microphone, fullscreen, geolocation, payment, etc.) was simply not enforced for documents embedded that way. A page could therefore host cross-origin content in a non-iframe embedder and use gated capabilities that Permissions Policy should deny — unexpected cross-origin behavior.

The fix computes isAllowedByFeaturePolicy for BOTH cases: iframe owners use iframe->featurePolicy().allows(…), and any other owner element falls back to FeaturePolicy::defaultPolicy(ownerElement->document()).allows(…), i.e. the default (no allow attribute) rules are applied to embed/frame owners; if neither allows it, the check fails. FeaturePolicy::parse is also changed to take the iframe by pointer (nullable) so it can be called for non-iframe owners, and the failure log message changes from ‘for iframe’ to ‘for element’.

The restored invariant is that feature/permissions policy is enforced for every embedding element, not just iframes.

The added test exercises getUserMedia inside an embed element.

Key insight
Feature-policy enforcement special-cased iframe owners and silently skipped embed/object/frame owners; applying the default policy to any non-iframe owner closes the cross-origin capability gap.

Attack Path

  1. Embed cross-origin content without an iframe Host cross-origin content via a non-iframe embedder (embed/object/frame) rather than an iframe.
  2. Skip the policy gate isFeaturePolicyAllowedByDocumentAndAllOwners only checked iframe owners, so the embedded document’s ancestor check is bypassed.
  3. Use a gated feature Invoke a permissions-policy-gated capability (camera, microphone, fullscreen, geolocation, payment) that should be denied cross-origin.
  4. Obtain cross-origin access The feature is granted despite policy, yielding unexpected cross-origin behavior / access to powerful features.

Impact Assessment

A Permissions/Feature Policy bypass confined to the WebContent process with no memory corruption: cross-origin content embedded via non-iframe elements escaped the policy gate and could reach features (camera, microphone, fullscreen, etc.) that should be denied. Impact is a privacy/policy-enforcement weakness (unexpected cross-origin capability access) rather than code execution.

Changed Functions

FunctionChangeNotes
isFeaturePolicyAllowedByDocumentAndAllOwners
Source/WebCore/html/FeaturePolicy.cpp
modified Applies the owner's feature policy for iframe owners and FeaturePolicy::defaultPolicy(...) for any other embedding element, instead of only checking HTMLIFrameElement owners.
FeaturePolicy::parse
Source/WebCore/html/FeaturePolicy.cpp
modified Takes the iframe as a nullable pointer so the default policy can be parsed/applied for non-iframe owners.
FeaturePolicy::defaultPolicy / parse (declarations)
Source/WebCore/html/FeaturePolicy.h
modified Exposes a default-policy path usable for embed/frame owners.

Files Changed

  • LayoutTests/fullscreen/full-screen-enabled-expected.txt
  • LayoutTests/fullscreen/full-screen-enabled-prefixed-expected.txt
  • LayoutTests/fullscreen/full-screen-iframe-not-allowed-expected.txt
  • LayoutTests/fullscreen/full-screen-iframe-without-allow-attribute-allowed-from-parent-expected.txt
  • LayoutTests/fullscreen/full-screen-restrictions-expected.txt
  • LayoutTests/http/tests/fullscreen/fullscreen-feature-policy-expected.txt
  • LayoutTests/http/tests/gamepad/gamepad-allow-attribute.https-expected.txt
  • LayoutTests/http/tests/media/media-stream/enumerate-devices-iframe-allow-attribute-expected.txt
  • LayoutTests/http/tests/media/media-stream/get-user-media-in-embed-element-expected.txt
  • LayoutTests/http/tests/media/media-stream/get-user-media-in-embed-element.html
  • LayoutTests/http/tests/media/media-stream/resources/get-user-media-embed.html
  • LayoutTests/http/tests/paymentrequest/payment-allow-attribute.https-expected.txt
  • LayoutTests/http/tests/security/sandboxed-iframe-geolocation-getCurrentPosition-expected.txt
  • LayoutTests/http/tests/security/sandboxed-iframe-geolocation-watchPosition-expected.txt
  • LayoutTests/http/tests/ssl/media-stream/get-user-media-different-host-expected.txt
  • LayoutTests/http/tests/ssl/media-stream/get-user-media-nested-expected.txt
  • LayoutTests/http/tests/webrtc/enumerateDevicesInFrames-expected.txt
  • LayoutTests/http/tests/webshare/webshare-allow-attribute-canShare.https-expected.txt
  • LayoutTests/http/tests/webshare/webshare-allow-attribute-share.https-expected.txt
  • LayoutTests/imported/w3c/web-platform-tests/html/semantics/embedded-content/the-iframe-element/iframe-allow-expected.txt
  • LayoutTests/imported/w3c/web-platform-tests/html/semantics/embedded-content/the-iframe-element/iframe-allowfullscreen-expected.txt
  • LayoutTests/imported/w3c/web-platform-tests/mediacapture-streams/MediaStream-feature-policy-none.https-expected.txt
  • LayoutTests/imported/w3c/web-platform-tests/permissions-policy/payment-allowed-by-permissions-policy-attribute-redirect-on-load.https.sub-expected.txt
  • LayoutTests/imported/w3c/web-platform-tests/screen-wake-lock/wakelock-enabled-by-feature-policy-attribute-redirect-on-load.https.sub-expected.txt
  • LayoutTests/imported/w3c/web-platform-tests/web-share/disabled-by-permissions-policy-cross-origin.https.sub-expected.txt
  • LayoutTests/platform/glib/imported/w3c/web-platform-tests/mediacapture-streams/MediaStream-feature-policy-none.https-expected.txt
  • LayoutTests/platform/glib/imported/w3c/web-platform-tests/screen-wake-lock/wakelock-enabled-by-feature-policy-attribute-redirect-on-load.https.sub-expected.txt
  • Source/WebCore/html/FeaturePolicy.cpp
  • Source/WebCore/html/FeaturePolicy.h

Audit Directions

  • Same function: owner-type handling
    Audit isFeaturePolicyAllowedByDocumentAndAllOwners and related ownerElement checks for other dynamicDowncast<HTMLIFrameElement> gates that ignore embed/object/frame owners.
  • Policy checks keyed on iframe
    Grep WebCore for featurePolicy()/allow-attribute logic that assumes an HTMLIFrameElement owner and misses other embedding elements.
diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog
index 409133b0ed61..e0a3e7cb2553 100644
--- a/LayoutTests/ChangeLog
+++ b/LayoutTests/ChangeLog
@@ -1,3 +1,13 @@
+2020-08-18  Antti Koivisto  <[email protected]>
+
+        The CSS specificity of :host() pseudo-classes is wrong
+        https://bugs.webkit.org/show_bug.cgi?id=202494
+        <rdar://problem/66292568>
+
+        Reviewed by Anders Carlsson.
+
+        * TestExpectations:
+
 2020-08-18  Diego Pino Garcia  <[email protected]>
 
         [GTK] Unreviewed test gardening. Update test baseline after r265749.
diff --git a/LayoutTests/TestExpectations b/LayoutTests/TestExpectations
index e17f48539072..1d87e2921dd0 100644
--- a/LayoutTests/TestExpectations
+++ b/LayoutTests/TestExpectations
@@ -4420,7 +4420,6 @@ webkit.org/b/214461 imported/w3c/web-platform-tests/css/css-pseudo/spelling-erro
 webkit.org/b/214462 imported/w3c/web-platform-tests/css/css-scoping/host-context-specificity-001.html [ ImageOnlyFailure ]
 webkit.org/b/214462 imported/w3c/web-platform-tests/css/css-scoping/host-context-specificity-002.html [ ImageOnlyFailure ]
 webkit.org/b/214462 imported/w3c/web-platform-tests/css/css-scoping/host-context-specificity-003.html [ ImageOnlyFailure ]
-webkit.org/b/214462 imported/w3c/web-platform-tests/css/css-scoping/host-specificity.html [ ImageOnlyFailure ]
 webkit.org/b/214462 imported/w3c/web-platform-tests/css/css-scoping/host-with-default-namespace-001.html [ ImageOnlyFailure ]
 webkit.org/b/214462 imported/w3c/web-platform-tests/css/css-scoping/shadow-directionality-001.tentative.html [ ImageOnlyFailure ]
 webkit.org/b/214462 imported/w3c/web-platform-tests/css/css-scoping/shadow-directionality-002.tentative.html [ ImageOnlyFailure ]
diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog
index 3433c7799ce4..383409fd4bc2 100644
--- a/Source/WebCore/ChangeLog
+++ b/Source/WebCore/ChangeLog
@@ -1,3 +1,18 @@
+2020-08-18  Antti Koivisto  <[email protected]>
+
+        The CSS specificity of :host() pseudo-classes is wrong
+        https://bugs.webkit.org/show_bug.cgi?id=202494
+        <rdar://problem/66292568>
+
+        Reviewed by Anders Carlsson.
+
+        https://drafts.csswg.org/css-scoping/#host-selector
+
+        “The specificity of :host() is that of a pseudo-class, plus the specificity of its argument.”
+
+        * css/CSSSelector.cpp:
+        (WebCore::simpleSelectorSpecificityInternal):
+
 2020-08-18  Youenn Fablet  <[email protected]>
 
         Add a JS built-in routine to mark a promise as handled
diff --git a/Source/WebCore/css/CSSSelector.cpp b/Source/WebCore/css/CSSSelector.cpp
index afd9bab0f9e6..c810b21d9c98 100644
--- a/Source/WebCore/css/CSSSelector.cpp
+++ b/Source/WebCore/css/CSSSelector.cpp
@@ -128,6 +128,7 @@ static unsigned simpleSelectorSpecificityInternal(const CSSSelector& simpleSelec
             return 0;
         case CSSSelector::PseudoClassNthChild:
         case CSSSelector::PseudoClassNthLastChild:
+        case CSSSelector::PseudoClassHost:
             return CSSSelector::addSpecificities(static_cast<unsigned>(SelectorSpecificityIncrement::ClassB), simpleSelector.selectorList() ? maxSpecificity(*simpleSelector.selectorList()) : 0);
         default:
             break;
Loading diff…

Original Bug Report

The reporter's bug is still restricted on the tracker.