Low watch CVSS 6.1 chrome Policy Bypass 📄 Reporter bug report 🔧 Commit mapped

Overview

Low
Severity
6.1
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactPolicy bypass in Audio
DescriptionPolicy bypass in Audio
ComponentAudio
Sub-componentMedia Controls
Bug ClassPolicy Bypass
Action tierwatch — logic/policy bug — security-relevant, no memory-unsafety
Computed by PatchGap's deterministic exploitability ruler from the fixing commit. patch-now = RCE-class & attacker-reachable in a default config, or exploited in the wild; watch = real security signal below that bar; noise = no security relevance.
Tracker40064543
Fix commitad95dd50a86d (chromium/src) +41/-0
CWECWE-693
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N
CISA KEVNot listed
CreditedLuan Herrera (@lbherrera_)
Reported2023-05-13 (per vendor advisory)
Fix landed2026-02-19 (public git)
Disclosed2026-04-07
Patch gap47 days — fix public in source before the advisory

Background

Sandboxed iframe download policy
the HTML sandbox attribute gates downloads behind the allow-downloads token; without it, content inside the frame is not permitted to initiate downloads.
Blink media controls
the default <video>/<audio> control UI rendered by Blink, which includes a download button element for saving the media resource.
`WebLocalFrame::IsAllowedToDownload`
a frame-level query exposing whether the current frame’s sandbox flags permit downloads.
`MediaControlDownloadButtonElement`
the Blink element (media_control_download_button_element.cc) representing the media-controls download button, whose display is decided by a predicate returning true/false.

Root Cause Analysis

The predicate that decides whether to show the media-controls download button (the method enclosing the edited hunk in media_control_download_button_element.cc; its name is not shown in the @@ context) evaluated its existing conditions and, if they passed, returned true without consulting the frame’s sandbox download policy. As a result a <video controls> placed inside an iframe sandboxed without allow-downloads still rendered a functional download button, letting the user save the media even though the sandbox was meant to disallow downloads.

The fix inserts a new guard: if GetDocument().GetFrame() exists, it resolves the corresponding WebLocalFrame via WebLocalFrame::FromFrameToken and returns false when !web_local_frame->IsAllowedToDownload(), placed before the final return true;. This tightens the predicate so the button is hidden whenever the sandbox forbids downloads, aligning the media-controls path with the sandbox policy. The shipped web test confirms the button is disabled in a sandbox="allow-same-origin" frame and enabled when allow-downloads is added.

Key insight
The media-controls download button was displayed without ever checking the frame’s sandbox download permission; the fix adds a IsAllowedToDownload check so the button is hidden (predicate returns false) inside download-disallowing sandboxed iframes.

Attack Path

  1. Embed media in a restrictive sandbox A page embeds an iframe with sandbox="allow-same-origin" (omitting allow-downloads) containing a <video controls> element.
  2. Default controls render the button Blink’s default media controls show the download button because the pre-patch display predicate never consulted the sandbox download flag.
  3. User saves the resource Activating the functional download button lets the media be downloaded from a context the sandbox attribute was intended to block, bypassing the allow-downloads policy.

Impact Assessment

The vendor advisory rates this low and describes it as “Policy bypass in Audio.” An attacker (or embedded content) gains the ability to offer a working download from inside a sandboxed iframe that was configured to disallow downloads, defeating the allow-downloads sandbox restriction; the effect is confined to the download-policy bypass with no memory-safety or privilege consequence shown in the patch. No exploited-in-the-wild status is claimed.

Changed Functions

FunctionChangeNotes
async_test
third_party/blink/web_tests/media/controls/download-button-hidden-when-sandboxed.html
modified
runAfterLayoutAndPaint
third_party/blink/web_tests/media/controls/download-button-hidden-when-sandboxed.html
modified

Files Changed

  • third_party/blink/renderer/modules/media_controls/elements/media_control_download_button_element.cc
  • third_party/blink/web_tests/media/controls/download-button-hidden-when-sandboxed.html

Audit Directions

  • UI affordances that bypass sandbox flags
    Audit other browser-provided controls (context-menu “Save as”, picture-in-picture, media/image save paths) for offering downloads without checking IsAllowedToDownload / the frame sandbox policy.
  • Policy checks missing on default UA UI
    Review Blink shadow-DOM user-agent controls that trigger privileged actions to confirm each re-validates the frame’s sandbox and permission state rather than assuming the embedder enforced it.
  • Frame-token resolution guards
    Where a new check depends on GetDocument().GetFrame() and WebLocalFrame::FromFrameToken possibly returning null, verify fail-closed behavior (null frame should not silently permit the action).
From ad95dd50a86da6f46e3ae113a487689e99f4c4cc Mon Sep 17 00:00:00 2001
From: Tommy Steimel <[email protected]>
Date: Thu, 19 Feb 2026 08:41:43 -0800
Subject: [PATCH] [Media Controls] Hide download button when sandbox flags disallow it

Currently, the default Blink media controls display a (functional)
download button, even when inside a sandboxed iframe that does not
allow downloads. This CL hides the download button when the element
is in a sandboxed iframe that disallows downloads.

Bug: 40064543
Change-Id: Ie25b48bb994ed66c8621429a3447cc279e67fbfb
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7590057
Commit-Queue: Tommy Steimel <[email protected]>
Reviewed-by: Benjamin Keen <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1587180}
---

diff --git a/third_party/blink/renderer/modules/media_controls/elements/media_control_download_button_element.cc b/third_party/blink/renderer/modules/media_controls/elements/media_control_download_button_element.cc
index fe23dd7e8..f670bf0 100644
--- a/third_party/blink/renderer/modules/media_controls/elements/media_control_download_button_element.cc
+++ b/third_party/blink/renderer/modules/media_controls/elements/media_control_download_button_element.cc
@@ -8,6 +8,7 @@
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/user_metrics_action.h"
 #include "third_party/blink/public/strings/grit/blink_strings.h"
+#include "third_party/blink/public/web/web_local_frame.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/dom/events/event.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
@@ -50,6 +51,14 @@
     return false;
   }
 
+  if (GetDocument().GetFrame()) {
+    WebLocalFrame* web_local_frame = WebLocalFrame::FromFrameToken(
+        GetDocument().GetFrame()->GetLocalFrameToken());
+    if (web_local_frame && !web_local_frame->IsAllowedToDownload()) {
+      return false;
+    }
+  }
+
   return true;
 }
 
diff --git a/third_party/blink/web_tests/media/controls/download-button-hidden-when-sandboxed.html b/third_party/blink/web_tests/media/controls/download-button-hidden-when-sandboxed.html
new file mode 100644
index 0000000..45839b23
--- /dev/null
+++ b/third_party/blink/web_tests/media/controls/download-button-hidden-when-sandboxed.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<title>media controls download button hidden when sandboxed</title>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../../resources/run-after-layout-and-paint.js"></script>
+<script src="../media-controls.js"></script>
+<iframe id="downloads-blocked"sandbox="allow-same-origin"></iframe>
+<iframe id="downloads-allowed"sandbox="allow-same-origin allow-downloads"></iframe>
+<script>
+async_test(function(t) {
+  const iframeBlocked = document.querySelector("#downloads-blocked");
+  const iframeAllowed = document.querySelector("#downloads-allowed");
+  const videoBlocked = iframeBlocked.contentDocument.createElement("video");
+  const videoAllowed = iframeAllowed.contentDocument.createElement("video");
+
+  [videoBlocked, videoAllowed].forEach((video) => {
+    video.controls = true;
+    video.setAttribute("preload", "none");
+    video.src = "https://someexample.example/example.mp4";
+  });
+  iframeBlocked.contentDocument.body.append(videoBlocked);
+  iframeAllowed.contentDocument.body.append(videoAllowed);
+
+  runAfterLayoutAndPaint(t.step_func_done(function() {
+    assert_false(isDownloadsButtonEnabled(videoBlocked),
+      "the download button should not show when the sandbox blocks downloads");
+    assert_true(isDownloadsButtonEnabled(videoAllowed),
+      "the download button should show when the sandbox allows downloads");
+  }));
+
+});
+</script>
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/third_party/blink/web_tests/media/controls/download-button-hidden-when-sandboxed.html b/third_party/blink/web_tests/media/controls/download-button-hidden-when-sandboxed.html
new file mode 100644
index 0000000..45839b23
--- /dev/null
+++ b/third_party/blink/web_tests/media/controls/download-button-hidden-when-sandboxed.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<title>media controls download button hidden when sandboxed</title>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../../resources/run-after-layout-and-paint.js"></script>
+<script src="../media-controls.js"></script>
+<iframe id="downloads-blocked"sandbox="allow-same-origin"></iframe>
+<iframe id="downloads-allowed"sandbox="allow-same-origin allow-downloads"></iframe>
+<script>
+async_test(function(t) {
+  const iframeBlocked = document.querySelector("#downloads-blocked");
+  const iframeAllowed = document.querySelector("#downloads-allowed");
+  const videoBlocked = iframeBlocked.contentDocument.createElement("video");
+  const videoAllowed = iframeAllowed.contentDocument.createElement("video");
+
+  [videoBlocked, videoAllowed].forEach((video) => {
+    video.controls = true;
+    video.setAttribute("preload", "none");
+    video.src = "https://someexample.example/example.mp4";
+  });
+  iframeBlocked.contentDocument.body.append(videoBlocked);
+  iframeAllowed.contentDocument.body.append(videoAllowed);
+
+  runAfterLayoutAndPaint(t.step_func_done(function() {
+    assert_false(isDownloadsButtonEnabled(videoBlocked),
+      "the download button should not show when the sandbox blocks downloads");
+    assert_true(isDownloadsButtonEnabled(videoAllowed),
+      "the download button should show when the sandbox allows downloads");
+  }));
+
+});
+</script>
Loading diff…

Original Bug Report

reported by [email protected]

Audio player's download functionality allows bypassing the "allow-downloads" flag of sandboxed iframes

VULNERABILITY DETAILS
A sandboxed iframe is not supposed to be able to initiate a download unless the “allow-downloads” flag is set. However, it is possible to make the user click on the “Download” option of the audio player through a clickjacking attack, which ends up downloading a file.

An arbitrary file can be made to be downloaded by performing a server-side redirect when the user tries to download the original audio file.

The “Download” option should be checking whether the iframe is sandboxed and contains the proper flags before deciding whether the download should initiate.

For the attack to work, it requires the victim to click twice inside a malicious iframe. In a real attack, there would be buttons placed over the video player to trick the user into clicking on the “three dots” and the “Download” option. The PoC just places opacity over the iframe to demonstrate that it would be possible to do that.

For reference, this issue is similar to https://crbug.com/chromium/1100761, albeit more severe as this issue doesn’t require any additional sandbox permission to work.

I have attached a video (repro.mkv) reproducing the attack.

VERSION
Chrome Version:
113.0.5672.92 (Official Build) stable (64-bit)
114.0.5735.26 (Official Build) beta (64-bit)
115.0.5762.4 (Official Build) dev (64-bit)

Operating System:
Ubuntu 20.04

REPRODUCTION CASE

  1. Download “index.html”, “iframe.html”, “iframe2.html”, “horse.ogg”, “file.txt”, “download.php” and place them in the same directory.
  2. Run the “php -S 0:8000” command in the directory the files were downloaded to.
  3. Access http://localhost:8000/
  4. Click on the “three dots” and then on the “Download” option, which will download a file even though the iframe doesn’t have the “allow-downloads” flag set.

CREDIT INFORMATION
Reporter credit: Luan Herrera (@lbherrera_)

View on issue tracker