Medium CVSS 4.3 webkit Logic Error 🔧 Commit mapped

Overview

Medium
Severity
4.3
CVSS
No
Exploited ITW
Fixed
Fix Status
DescriptionVisiting a maliciously crafted website may lead to a cross-site scripting attack
ComponentWebCore DOM
Bug ClassLogic Error
Tracker305859
Fix commit59efb640749a (WebKit/WebKit) +38/-5
CWECWE-79 (Cross-site scripting)
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:N/A:N
CISA KEVNot listed
Credited@hamayanhamayan
Disclosed2026-03-24

Background

MediaDocument
A synthetic WebCore document that wraps a media resource URL in a media element and never parses the payload as HTML, so any markup/script in the resource is inert.
MediaPlayer::supportsType
A runtime query that asks the available media engines whether they can play a given content type, returning IsNotSupported / MayBeSupported / IsSupported and depending on installed codecs and platform state.
MIMETypeRegistry::isSupportedMediaMIMEType
A static, table-driven predicate that reports whether a MIME type is a known/supported media type, used across WebKit to classify resources independently of live engine state.
AVStreamDataParserMIMETypeCache
A cache of MIME types the platform AVStreamDataParser (Media Source Extensions backend) reports it can parse, a dynamic and OS-version-dependent list.
Cross-site scripting (XSS)
Execution of attacker-controlled script in the security context (origin) of a target document, here achieved by getting HTML+script rendered as active content instead of an inert media document.

Root Cause Analysis

DOMImplementation::createDocument() decides what kind of Document to instantiate for a given content type. For media types it previously called the live media-engine query MediaPlayer::supportsType(parameters) and, if the result was anything other than IsNotSupported, created a MediaDocument (a synthetic document that merely wraps the URL in a <video> element and never parses the payload as markup).

The bug is that this check was NOT the same check the rest of WebKit uses to classify a resource as ‘media’: other code paths rely on MIMETypeRegistry::isSupportedMediaMIMEType(). Because the two predicates could disagree for a given MIME type, a resource could be classified one way for loading/handling and the opposite way for document creation. When a type that the registry considered ‘media’ (so the response was allowed through as media rather than sniffed/handled as HTML) fell through createDocument()’s divergent MediaPlayer::supportsType() test as IsNotSupported, WebKit did not build a safe MediaDocument and instead fell through to building a normal document that parses and executes the payload’s HTML and scripts. The bundled LayoutTest demonstrates the effect precisely: an iframe is pointed at ‘data:video/mp2t,<h1>Error</h1><script>parent.postMessage(“fail”,"*")</script>’, and the test only passes if that script does NOT run (no postMessage) while the document still reaches readyState ‘complete’ — i.e. the payload must be handled as an inert MediaDocument, not executed.

The fix restores a single, consistent classification: createDocument() now calls MIMETypeRegistry::isSupportedMediaMIMEType(contentType), the same predicate used elsewhere, so anything treated as media is uniformly turned into a MediaDocument.

The patch additionally hardens isSupportedMediaMIMEType() to reject any type whose lowercased form does not start with ‘video/’, ‘audio/’, or ‘application/’ before consulting the supported set, eliminating non-media prefixes from ever being accepted as media. Finally, MediaPlayerPrivateMediaSourceAVFObjC::getSupportedTypes() is changed to clear the set instead of returning AVStreamDataParserMIMETypeCache’s dynamically reported types; that dynamic, OS-dependent list was a source of divergence between MediaPlayer::supportsType() and the static registry, so removing it narrows the set of types whose media-vs-HTML disposition could differ across code paths. The violated invariant is ‘a resource classified as a supported media type is always rendered as an inert MediaDocument, never parsed as active content’; the fix re-establishes it by using one authoritative predicate and by shrinking the fuzzy, engine-dependent portion of the supported-type universe.

Key insight
The vulnerability is a classifier-divergence bug: two different predicates (live MediaPlayer::supportsType vs the static MIMETypeRegistry) disagreed about whether a MIME type is ‘media’, so a resource admitted as media could still be built as an executable document — HTML labeled as media got parsed and its script ran. Consistency of a security-relevant type decision across code paths is the real invariant.

Attack Path

  1. Choose a divergent media MIME type Identify a MIME type (e.g. video/mp2t as used by the test) that MIMETypeRegistry treats as a supported media type — so the response is admitted as media and not sniffed as HTML — but for which the pre-patch MediaPlayer::supportsType() engine query returns IsNotSupported.
  2. Serve HTML+script under that type Deliver a payload whose bytes are HTML containing an inline <script>, but label it with the chosen media MIME type. The LayoutTest uses a data: URL (‘data:video/mp2t,<h1>Error</h1><script>…</script>’); a server response with Content-Type: video/mp2t works equivalently.
  3. Load it as a document Cause the payload to be loaded as a document — e.g. in an iframe, a navigation, or a subframe the attacker controls the response for — so DOMImplementation::createDocument() runs for that content type.
  4. Divergent check misfires createDocument()’s MediaPlayer::supportsType() returns IsNotSupported for the type, so it skips MediaDocument::create() and falls through to building a document that parses the payload as markup.
  5. Script executes The inline script runs in the resulting document’s context; the test detects this via a postMessage to the parent. In a real attack this yields script execution under whatever origin the response was served from, enabling cross-site scripting (e.g. against a site that lets users host files it believes will be treated as inert media).

Impact Assessment

This is a same-origin/type-confusion logic bug, not a memory-corruption primitive: the payoff is arbitrary script execution in the origin under which the mislabeled response is served, i.e. cross-site scripting, entirely within the WebContent process and its sandbox. It does not by itself give an out-of-bounds or use-after-free primitive and cannot be escalated to native code execution through this path alone. Realistic impact is against sites that accept user-controlled content and rely on serving it under a supposedly inert media MIME type; there it enables full XSS (cookie/session theft, DOM access) in that site’s context.

Changed Functions

FunctionChangeNotes
DOMImplementation::createDocument
Source/WebCore/dom/DOMImplementation.cpp
modified Replaced the MediaEngineSupportParameters + MediaPlayer::supportsType() query with a single MIMETypeRegistry::isSupportedMediaMIMEType(contentType) call to decide whether to build a MediaDocument, aligning document creation with the rest of WebKit's media-type classification.
MIMETypeRegistry::isSupportedMediaMIMEType
Source/WebCore/platform/MIMETypeRegistry.cpp
modified Added a guard that lowercases the type and returns false unless it starts with 'video/', 'audio/', or 'application/', before checking supportedMediaMIMETypes(), preventing non-media prefixes from being accepted as media.
MediaPlayerPrivateMediaSourceAVFObjC::getSupportedTypes
Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaSourceAVFObjC.mm
modified Now clears the output set instead of returning AVStreamDataParserMIMETypeCache::singleton().supportedTypes(), removing the dynamic OS-dependent type list that was a source of divergent media-type support answers.

Files Changed

  • LayoutTests/media/iframe-load-html-as-m2ts-expected.txt
  • LayoutTests/media/iframe-load-html-as-m2ts.html
  • Source/WebCore/dom/DOMImplementation.cpp
  • Source/WebCore/platform/MIMETypeRegistry.cpp
  • Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaSourceAVFObjC.mm

Audit Directions

  • Other createDocument-style media decisions
    Grep for remaining callers of MediaPlayer::supportsType and MediaEngineSupportParameters in document/loader factory paths (DOMImplementation.cpp, DocumentLoader, FrameLoader, PluginDocument) and check whether the media/plugin-vs-HTML decision there uses the same authoritative predicate now used in createDocument.
  • Predicate mismatches across the MIME registry
    Audit every use of MIMETypeRegistry::isSupportedMediaMIMEType, isSupportedImageMIMEType, and the plugin/PDF equivalents for places where one code path decides ’this resource is X’ and another decides how to render it, looking for asymmetry that lets HTML be admitted under a non-HTML type.
  • Dynamically populated supported-type sets
    Search for getSupportedTypes implementations that copy from OS/engine caches (AVStreamDataParserMIMETypeCache, AVAssetMIMETypeCache, and similar ‘singleton().supportedTypes()’ patterns) and verify they cannot inject types with non-media prefixes or types that other classifiers do not also recognize.
  • Prefix/normalization assumptions on MIME types
    Look for MIME-type comparisons that do not normalize case or do not constrain the top-level type (missing convertToASCIILowercase() or missing startsWith(“video/”/“audio/”) checks) in supportedX MIMEType functions, since the fix shows these were exploitable gaps.

Original Bug Report

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