Chrome · Network
CVE-2026-79253
Logic Error in Network
Overview
Low
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Files Changed
chrome/browser/download/download_target_determiner_unittest.cccontent/browser/file_system_access/file_system_chooser.cccontent/browser/web_contents/web_contents_view_aura_unittest.ccnet/base/filename_util_internal.ccnet/base/filename_util_unittest.cc
Patch
From a94fdc22a1e11467b08cbc4bc5b8e062ae8bc358 Mon Sep 17 00:00:00 2001
From: Etienne Bergeron <etienneb@google.com>
Date: Fri, 17 Jul 2026 07:38:20 -0700
Subject: [PATCH] [net/base] Block .scf and .url files in IsShellIntegratedExtension
A divergence between net::IsShellIntegratedExtension() and
FileSystemChooser::IsShellIntegratedExtension() allowed .url and .scf
files to bypass extension sanitization during standard downloads.
Windows Explorer automatically parses .url and .scf files upon folder
view (without the user double-clicking them) to retrieve metadata
or render icons. If a downloaded .url contains an IconFile pointing
to a remote UNC path, simply viewing the Downloads directory triggers
an automatic outbound SMB connection, leading to a disclosure of the
victim's NetNTLMv2 credential hash.
This CL adds "scf" and "url" to net::IsShellIntegratedExtension(),
ensuring they are correctly renamed to ".download" on Windows during
standard downloads.
Bug: 533511921
Test: net_unittests --gtest_filter=FilenameUtilTest.*
Change-Id: Iff138ff80003999fd3f447dffec1647d45ab2b1d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8086934
Reviewed-by: Kenichi Ishibashi <bashi@chromium.org>
Reviewed-by: Arthur Sonzogni <arthursonzogni@chromium.org>
Commit-Queue: Etienne Bergeron <etienneb@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1663888}
---
diff --git a/chrome/browser/download/download_target_determiner_unittest.cc b/chrome/browser/download/download_target_determiner_unittest.cc
index 07a025c..b113eb7 100644
--- a/chrome/browser/download/download_target_determiner_unittest.cc
+++ b/chrome/browser/download/download_target_determiner_unittest.cc
@@ -2839,7 +2839,8 @@
DownloadItem::TARGET_DISPOSITION_PROMPT,
EXPECT_CRDOWNLOAD},
- {// 2: File name falling back to dangerous extensions after removing env var.
+ {// 2: File name falling back to dangerous extensions after removing env
+ // var.
SAVE_AS, download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadFileType::NOT_DANGEROUS, "http://example.com/foo2.lnk.%%",
"application/octet-stream", FILE_PATH_LITERAL(""),
@@ -2848,7 +2849,8 @@
DownloadItem::TARGET_DISPOSITION_PROMPT,
EXPECT_CRDOWNLOAD},
- {// 3: Double extension bug leading to dangerous extensions after removing env var.
+ {// 3: Double extension bug leading to dangerous extensions after removing
+ // env var.
SAVE_AS, download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadFileType::NOT_DANGEROUS, "http://example.com/foo2.lnk %%",
"application/octet-stream", FILE_PATH_LITERAL(""),
@@ -2857,8 +2859,9 @@
DownloadItem::TARGET_DISPOSITION_PROMPT,
EXPECT_CRDOWNLOAD},
- {// 4: Unicode char bug leading to dangerous extensions after removing env var.
- // NOTE: The space before "%%" is a non-breaking space (U+00A0), not a normal space.
+ {// 4: Unicode char bug leading to dangerous extensions after removing env
+ // var. NOTE: The space before "%%" is a non-breaking space (U+00A0), not
+ // a normal space.
SAVE_AS, download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadFileType::NOT_DANGEROUS, "http://example.com/foo2.lnk %%",
"application/octet-stream", FILE_PATH_LITERAL(""),
@@ -2891,7 +2894,7 @@
"http://example.com/photo.jpg%20%25%25.url", "text/plain",
FILE_PATH_LITERAL(""),
- FILE_PATH_LITERAL("photo.jpg.url"),
+ FILE_PATH_LITERAL("photo.jpg.download"),
DownloadItem::TARGET_DISPOSITION_PROMPT,
EXPECT_CRDOWNLOAD},
diff --git a/content/browser/file_system_access/file_system_chooser.cc b/content/browser/file_system_access/file_system_chooser.cc
index 897ae7f..d2bf686 100644
--- a/content/browser/file_system_access/file_system_chooser.cc
+++ b/content/browser/file_system_access/file_system_chooser.cc
@@ -388,12 +388,14 @@
// https://crbug.com/1227995, respectively). '.local' files are used by
// Windows to determine which DLLs to load for an application. '.url' files
// can be used to read arbirtary files (see https://crbug.com/1307930).
+ // LINT.IfChange(ShellIntegratedExtensions)
if ((extension_lower == FILE_PATH_LITERAL("lnk")) ||
(extension_lower == FILE_PATH_LITERAL("local")) ||
(extension_lower == FILE_PATH_LITERAL("scf")) ||
(extension_lower == FILE_PATH_LITERAL("url"))) {
return true;
}
+ // LINT.ThenChange(//net/base/filename_util_internal.cc:ShellIntegratedExtensions)
// Setting a file's extension to a CLSID may conceal its actual file type on
// some Windows versions (see https://nvd.nist.gov/vuln/detail/CVE-2004-0420).
diff --git a/content/browser/web_contents/web_contents_view_aura_unittest.cc b/content/browser/web_contents/web_contents_view_aura_unittest.cc
index 5eb6c0c..ce85bc8d 100644
--- a/content/browser/web_contents/web_contents_view_aura_unittest.cc
+++ b/content/browser/web_contents/web_contents_view_aura_unittest.cc
@@ -711,7 +711,7 @@
data->GetVirtualFilenames();
ASSERT_TRUE(file_infos.has_value());
ASSERT_EQ(1ULL, file_infos.value().size());
- EXPECT_EQ(base::FilePath(base::UTF16ToWide(url_title) + L".url"),
+ EXPECT_EQ(base::FilePath(base::UTF16ToWide(url_title) + L".download"),
file_infos.value()[0].display_name);
ui::DropTargetEvent event(*data.get(), kClientPt, kScreenPt,
diff --git a/net/base/filename_util_internal.cc b/net/base/filename_util_internal.cc
index d5799604..1d3c9e6 100644
--- a/net/base/filename_util_internal.cc
+++ b/net/base/filename_util_internal.cc
@@ -155,12 +155,20 @@
bool IsShellIntegratedExtension(const base::FilePath::StringType& extension) {
base::FilePath::StringType extension_lower = base::ToLowerASCII(extension);
- // .lnk files may be used to execute arbitrary code (see
- // https://nvd.nist.gov/vuln/detail/CVE-2010-2568). .local files are used by
- // Windows to determine which DLLs to load for an application.
+ // .lnk and .scf files may be used to execute arbitrary code (see
+ // https://nvd.nist.gov/vuln/detail/CVE-2010-2568 and
+ // https://crbug.com/1227995, respectively). .local files are used by
+ // Windows to determine which DLLs to load for an application. .url files
+ // can be used to leak credentials or read arbitrary files (see
+ // https://crbug.com/1307930).
+ // LINT.IfChange(ShellIntegratedExtensions)
if ((extension_lower == FILE_PATH_LITERAL("local")) ||
- (extension_lower == FILE_PATH_LITERAL("lnk")))
+ (extension_lower == FILE_PATH_LITERAL("lnk")) ||
+ (extension_lower == FILE_PATH_LITERAL("scf")) ||
+ (extension_lower == FILE_PATH_LITERAL("url"))) {
return true;
+ }
+ // LINT.ThenChange(//content/browser/file_system_access/file_system_chooser.cc:ShellIntegratedExtensions)
// Setting a file's extension to a CLSID may conceal its actual file type on
// some Windows versions (see https://nvd.nist.gov/vuln/detail/CVE-2004-0420).
diff --git a/net/base/filename_util_unittest.cc b/net/base/filename_util_unittest.cc
index ca470af..6f06b6c0 100644
--- a/net/base/filename_util_unittest.cc
+++ b/net/base/filename_util_unittest.cc
@@ -109,6 +109,8 @@
FILE_PATH_LITERAL(" Computer"),
FILE_PATH_LITERAL("My Computer.{a}"),
FILE_PATH_LITERAL("My Computer.{20D04FE0-3AEA-1069-A2D8-08002B30309D}"),
+ FILE_PATH_LITERAL("harmless.scf"),
+ FILE_PATH_LITERAL("harmless.url"),
#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
FILE_PATH_LITERAL("a\\a"),
#endif
@@ -355,41 +357,45 @@
const char* filename;
const char* expected_filename;
} safe_tests[] = {
- {__LINE__, "text/html", "bar.htm", "bar.htm"},
- {__LINE__, "text/html", "bar.html", "bar.html"},
- {__LINE__, "application/x-chrome-extension", "bar", "bar.crx"},
- {__LINE__, "image/png", "bar.html", "bar.html"},
- {__LINE__, "text/html", "bar.exe", "bar.exe"},
- {__LINE__, "image/gif", "bar.exe", "bar.exe"},
- {__LINE__, "text/html", "google.com", "google.com"},
- // Allow extension synonyms.
- {__LINE__, "image/jpeg", "bar.jpg", "bar.jpg"},
- {__LINE__, "image/jpeg", "bar.jpeg", "bar.jpeg"},
+ {__LINE__, "text/html", "bar.htm", "bar.htm"},
+ {__LINE__, "text/html", "bar.html", "bar.html"},
+ {__LINE__, "application/x-chrome-extension", "bar", "bar.crx"},
+ {__LINE__, "image/png", "bar.html", "bar.html"},
+ {__LINE__, "text/html", "bar.exe", "bar.exe"},
+ {__LINE__, "image/gif", "bar.exe", "bar.exe"},
+ {__LINE__, "text/html", "google.com", "google.com"},
+ // Allow extension synonyms.
+ {__LINE__, "image/jpeg", "bar.jpg", "bar.jpg"},
+ {__LINE__, "image/jpeg", "bar.jpeg", "bar.jpeg"},
#if BUILDFLAG(IS_WIN)
- // Device names
- {__LINE__, "text/html", "con.htm", "_con.htm"},
- {__LINE__, "text/html", "lpt1.htm", "_lpt1.htm"},
- {__LINE__, "application/x-chrome-extension", "con", "_con.crx"},
+ // Device names
+ {__LINE__, "text/html", "con.htm", "_con.htm"},
+ {__LINE__, "text/html", "lpt1.htm", "_lpt1.htm"},
+ {__LINE__, "application/x-chrome-extension", "con", "_con.crx"},
- // Looks like foo.{GUID} which get treated as namespace mounts on Windows.
- {__LINE__, "text/html", "harmless.{not-really-this-may-be-a-guid}",
- "harmless.download"},
- {__LINE__, "text/html", "harmless.{mismatched-", "harmless.{mismatched-"},
+ // Looks like foo.{GUID} which get treated as namespace mounts on Windows.
+ {__LINE__, "text/html", "harmless.{not-really-this-may-be-a-guid}",
+ "harmless.download"},
+ {__LINE__, "text/html", "harmless.{mismatched-", "harmless.{mismatched-"},
- // Dangerous extensions
- {__LINE__, "text/html", "harmless.local", "harmless.download"},
- {__LINE__, "text/html", "harmless.lnk", "harmless.download"},
+ // Dangerous extensions
+ {__LINE__, "text/html", "harmless.local", "harmless.download"},
+ {__LINE__, "text/html", "harmless.lnk", "harmless.download"},
+ {__LINE__, "text/html", "harmless.scf", "harmless.download"},
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 07a025c..b113eb7 100644
--- a/chrome/browser/download/download_target_determiner_unittest.cc
+++ b/chrome/browser/download/download_target_determiner_unittest.cc
@@ -2839,7 +2839,8 @@
DownloadItem::TARGET_DISPOSITION_PROMPT,
EXPECT_CRDOWNLOAD},
- {// 2: File name falling back to dangerous extensions after removing env var.
+ {// 2: File name falling back to dangerous extensions after removing env
+ // var.
SAVE_AS, download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadFileType::NOT_DANGEROUS, "http://example.com/foo2.lnk.%%",
"application/octet-stream", FILE_PATH_LITERAL(""),
@@ -2848,7 +2849,8 @@
DownloadItem::TARGET_DISPOSITION_PROMPT,
EXPECT_CRDOWNLOAD},
- {// 3: Double extension bug leading to dangerous extensions after removing env var.
+ {// 3: Double extension bug leading to dangerous extensions after removing
+ // env var.
SAVE_AS, download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadFileType::NOT_DANGEROUS, "http://example.com/foo2.lnk %%",
"application/octet-stream", FILE_PATH_LITERAL(""),
@@ -2857,8 +2859,9 @@
DownloadItem::TARGET_DISPOSITION_PROMPT,
EXPECT_CRDOWNLOAD},
- {// 4: Unicode char bug leading to dangerous extensions after removing env var.
- // NOTE: The space before "%%" is a non-breaking space (U+00A0), not a normal space.
+ {// 4: Unicode char bug leading to dangerous extensions after removing env
+ // var. NOTE: The space before "%%" is a non-breaking space (U+00A0), not
+ // a normal space.
SAVE_AS, download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
DownloadFileType::NOT_DANGEROUS, "http://example.com/foo2.lnk %%",
"application/octet-stream", FILE_PATH_LITERAL(""),
@@ -2891,7 +2894,7 @@
"http://example.com/photo.jpg%20%25%25.url", "text/plain",
FILE_PATH_LITERAL(""),
- FILE_PATH_LITERAL("photo.jpg.url"),
+ FILE_PATH_LITERAL("photo.jpg.download"),
DownloadItem::TARGET_DISPOSITION_PROMPT,
EXPECT_CRDOWNLOAD},
diff --git a/content/browser/web_contents/web_contents_view_aura_unittest.cc b/content/browser/web_contents/web_contents_view_aura_unittest.cc
index 5eb6c0c..ce85bc8d 100644
--- a/content/browser/web_contents/web_contents_view_aura_unittest.cc
+++ b/content/browser/web_contents/web_contents_view_aura_unittest.cc
@@ -711,7 +711,7 @@
data->GetVirtualFilenames();
ASSERT_TRUE(file_infos.has_value());
ASSERT_EQ(1ULL, file_infos.value().size());
- EXPECT_EQ(base::FilePath(base::UTF16ToWide(url_title) + L".url"),
+ EXPECT_EQ(base::FilePath(base::UTF16ToWide(url_title) + L".download"),
file_infos.value()[0].display_name);
ui::DropTargetEvent event(*data.get(), kClientPt, kScreenPt,
diff --git a/net/base/filename_util_unittest.cc b/net/base/filename_util_unittest.cc
index ca470af..6f06b6c0 100644
--- a/net/base/filename_util_unittest.cc
+++ b/net/base/filename_util_unittest.cc
@@ -109,6 +109,8 @@
FILE_PATH_LITERAL(" Computer"),
FILE_PATH_LITERAL("My Computer.{a}"),
FILE_PATH_LITERAL("My Computer.{20D04FE0-3AEA-1069-A2D8-08002B30309D}"),
+ FILE_PATH_LITERAL("harmless.scf"),
+ FILE_PATH_LITERAL("harmless.url"),
#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
FILE_PATH_LITERAL("a\\a"),
#endif
@@ -355,41 +357,45 @@
const char* filename;
const char* expected_filename;
} safe_tests[] = {
- {__LINE__, "text/html", "bar.htm", "bar.htm"},
- {__LINE__, "text/html", "bar.html", "bar.html"},
- {__LINE__, "application/x-chrome-extension", "bar", "bar.crx"},
- {__LINE__, "image/png", "bar.html", "bar.html"},
- {__LINE__, "text/html", "bar.exe", "bar.exe"},
- {__LINE__, "image/gif", "bar.exe", "bar.exe"},
- {__LINE__, "text/html", "google.com", "google.com"},
- // Allow extension synonyms.
- {__LINE__, "image/jpeg", "bar.jpg", "bar.jpg"},
- {__LINE__, "image/jpeg", "bar.jpeg", "bar.jpeg"},
+ {__LINE__, "text/html", "bar.htm", "bar.htm"},
+ {__LINE__, "text/html", "bar.html", "bar.html"},
+ {__LINE__, "application/x-chrome-extension", "bar", "bar.crx"},
+ {__LINE__, "image/png", "bar.html", "bar.html"},
+ {__LINE__, "text/html", "bar.exe", "bar.exe"},
+ {__LINE__, "image/gif", "bar.exe", "bar.exe"},
+ {__LINE__, "text/html", "google.com", "google.com"},
+ // Allow extension synonyms.
+ {__LINE__, "image/jpeg", "bar.jpg", "bar.jpg"},
+ {__LINE__, "image/jpeg", "bar.jpeg", "bar.jpeg"},
#if BUILDFLAG(IS_WIN)
- // Device names
- {__LINE__, "text/html", "con.htm", "_con.htm"},
- {__LINE__, "text/html", "lpt1.htm", "_lpt1.htm"},
- {__LINE__, "application/x-chrome-extension", "con", "_con.crx"},
+ // Device names
+ {__LINE__, "text/html", "con.htm", "_con.htm"},
+ {__LINE__, "text/html", "lpt1.htm", "_lpt1.htm"},
+ {__LINE__, "application/x-chrome-extension", "con", "_con.crx"},
- // Looks like foo.{GUID} which get treated as namespace mounts on Windows.
- {__LINE__, "text/html", "harmless.{not-really-this-may-be-a-guid}",
- "harmless.download"},
- {__LINE__, "text/html", "harmless.{mismatched-", "harmless.{mismatched-"},
+ // Looks like foo.{GUID} which get treated as namespace mounts on Windows.
+ {__LINE__, "text/html", "harmless.{not-really-this-may-be-a-guid}",
+ "harmless.download"},
+ {__LINE__, "text/html", "harmless.{mismatched-", "harmless.{mismatched-"},
- // Dangerous extensions
- {__LINE__, "text/html", "harmless.local", "harmless.download"},
- {__LINE__, "text/html", "harmless.lnk", "harmless.download"},
+ // Dangerous extensions
+ {__LINE__, "text/html", "harmless.local", "harmless.download"},
+ {__LINE__, "text/html", "harmless.lnk", "harmless.download"},
+ {__LINE__, "text/html", "harmless.scf", "harmless.download"},
+ {__LINE__, "text/html", "harmless.url", "harmless.download"},
#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
- // On Posix, none of the above set is particularly dangerous.
- {__LINE__, "text/html", "con.htm", "con.htm"},
- {__LINE__, "text/html", "lpt1.htm", "lpt1.htm"},
- {__LINE__, "application/x-chrome-extension", "con", "con.crx"},
- {__LINE__, "text/html", "harmless.{not-really-this-may-be-a-guid}",
- "harmless.{not-really-this-may-be-a-guid}"},
- {__LINE__, "text/html", "harmless.{mismatched-", "harmless.{mismatched-"},
- {__LINE__, "text/html", "harmless.local", "harmless.local"},
- {__LINE__, "text/html", "harmless.lnk", "harmless.lnk"},
+ // On Posix, none of the above set is particularly dangerous.
+ {__LINE__, "text/html", "con.htm", "con.htm"},
+ {__LINE__, "text/html", "lpt1.htm", "lpt1.htm"},
+ {__LINE__, "application/x-chrome-extension", "con", "con.crx"},
+ {__LINE__, "text/html", "harmless.{not-really-this-may-be-a-guid}",
+ "harmless.{not-really-this-may-be-a-guid}"},
+ {__LINE__, "text/html", "harmless.{mismatched-", "harmless.{mismatched-"},
+ {__LINE__, "text/html", "harmless.local", "harmless.local"},
+ {__LINE__, "text/html", "harmless.lnk", "harmless.lnk"},
+ {__LINE__, "text/html", "harmless.scf", "harmless.scf"},
+ {__LINE__, "text/html", "harmless.url", "harmless.url"},
#endif // BUILDFLAG(IS_WIN)
};
Loading diff…
Original Bug Report
The reporter's bug is still restricted on the tracker. Chrome de-restricts security bugs ~30–90 days after the fix ships; a later run will backfill it here.
References
On This Page