Medium chrome Logic Error 📄 Reporter bug report 🔧 Commit mapped

Overview

Medium
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInsufficient validation of untrusted input in Downloads
DescriptionInsufficient validation of untrusted input in Downloads
ComponentDownloads
Bug ClassLogic Error
Tracker486079015
Fix commit8c1145d4b23d (chromium/src) +21/-6
CISA KEVNot listed
Crediteddaffainfo
Disclosed2026-04-07

Files Changed

  • chrome/browser/download/download_target_determiner.cc
  • chrome/browser/download/download_target_determiner_unittest.cc
From 8c1145d4b23d6a8cf2867b922716153f6ea72d9e Mon Sep 17 00:00:00 2001
From: Andrew Liu <liu@chromium.org>
Date: Wed, 04 Mar 2026 12:47:47 -0800
Subject: [PATCH] Filter out multiple "double percent signs" when normalizing download files

Bug: 486079015
Change-Id: I7047e2f0a210de1ae17cbb95884f3a6c6a6a6964
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7629495
Reviewed-by: Min Qin <qinmin@chromium.org>
Reviewed-by: Lily Chen <chlily@chromium.org>
Commit-Queue: Andrew Liu <liu@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1594178}
---

diff --git a/chrome/browser/download/download_target_determiner.cc b/chrome/browser/download/download_target_determiner.cc
index 760025c..1dd2dbab 100644
--- a/chrome/browser/download/download_target_determiner.cc
+++ b/chrome/browser/download/download_target_determiner.cc
@@ -599,13 +599,19 @@
       // file name first.
       std::wstring sanitized_name = ui::RemoveEnvVarFromFileName<wchar_t>(
           virtual_path_.BaseName().value(), L"%");
-      // remove leading "." to avoid resorting to potential extension
-      // bug: 41486690
-      while (!sanitized_name.empty() && sanitized_name.back() == L'.') {
-          sanitized_name.pop_back();
+      // Remove trailing "." and whitespace to avoid resorting to potential
+      // extensions.
+      // See crbug.com/41486690 and crbug.com/486079015 for more context.
+      while (!sanitized_name.empty()) {
+        size_t length = sanitized_name.length();
+        while (!sanitized_name.empty() && sanitized_name.back() == L'.') {
+            sanitized_name.pop_back();
+        }
+        // trim trailing whitespace (space, tab, NBSP) to prevent stale extensions
+        base::TrimWhitespace(sanitized_name, base::TrimPositions::TRIM_TRAILING, &sanitized_name);
+        if (length == sanitized_name.length())
+          break;
       }
-      // trim trailing whitespace (space, tab, NBSP) to prevent stale extensions
-      base::TrimWhitespace(sanitized_name, base::TrimPositions::TRIM_TRAILING, &sanitized_name);
       if (sanitized_name.empty()) {
         sanitized_name = base::UTF8ToWide(
             l10n_util::GetStringUTF8(IDS_DEFAULT_DOWNLOAD_FILENAME));
diff --git a/chrome/browser/download/download_target_determiner_unittest.cc b/chrome/browser/download/download_target_determiner_unittest.cc
index dff43fc..890c8305 100644
--- a/chrome/browser/download/download_target_determiner_unittest.cc
+++ b/chrome/browser/download/download_target_determiner_unittest.cc
@@ -2712,6 +2712,15 @@
 
        FILE_PATH_LITERAL("download"), DownloadItem::TARGET_DISPOSITION_PROMPT,
 
+       EXPECT_CRDOWNLOAD},
+      {// 6: Multiple env vars need to be filtered out.
+       SAVE_AS, download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
+       DownloadFileType::NOT_DANGEROUS, "http://example.com/foo.lnk .%% .%%",
+       "application/octet-stream", FILE_PATH_LITERAL(""),
+
+       FILE_PATH_LITERAL("foo.download"),
+       DownloadItem::TARGET_DISPOSITION_PROMPT,
+
        EXPECT_CRDOWNLOAD}};
 
   RunTestCasesWithActiveItem(kSaveEnvPathTestCases);
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/chrome/browser/download/download_target_determiner_unittest.cc b/chrome/browser/download/download_target_determiner_unittest.cc
index dff43fc..890c8305 100644
--- a/chrome/browser/download/download_target_determiner_unittest.cc
+++ b/chrome/browser/download/download_target_determiner_unittest.cc
@@ -2712,6 +2712,15 @@
 
        FILE_PATH_LITERAL("download"), DownloadItem::TARGET_DISPOSITION_PROMPT,
 
+       EXPECT_CRDOWNLOAD},
+      {// 6: Multiple env vars need to be filtered out.
+       SAVE_AS, download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
+       DownloadFileType::NOT_DANGEROUS, "http://example.com/foo.lnk .%% .%%",
+       "application/octet-stream", FILE_PATH_LITERAL(""),
+
+       FILE_PATH_LITERAL("foo.download"),
+       DownloadItem::TARGET_DISPOSITION_PROMPT,
+
        EXPECT_CRDOWNLOAD}};
 
   RunTestCasesWithActiveItem(kSaveEnvPathTestCases);
Loading diff…

Original Bug Report

reported by mu...@gmail.com

.lnk File Download Bypass Using "Save As" Link (Bypass of #444803530)

Steps to reproduce the problem

  1. Run poc.py, and then open http://127.0.0.1:8000/
  2. Right click download me hyperlink and then choose Save File As
  3. Choose Save button and .lnk file will be downloaded

Problem Description

This is a bypass of https://issues.chromium.org/issues/444803530, where an attacker uses a double percent sign (%%) to bypass Chrome’s dangerous file protection. Although it has already been fixed, it can still be bypassed by using a double “double percent sign,” such as: file.lnk .%% .%%

Summary

.lnk File Download Bypass Using “Save As” Link (Bypass of #444803530)

Additional Data

Category: Security
Chrome Channel: Not sure
Regression: N/A \

View on issue tracker
Links in the report