CVE-2026-79259
Overview
Changed Functions
| Function | Change | Notes |
|---|---|---|
TEST_Fchrome/services/file_util/public/cpp/sandboxed_seven_zip_analyzer_unittest.cc |
modified | |
ifchrome/utility/safe_browsing/archive_analyzer.cc |
modified |
Files Changed
chrome/services/file_util/public/cpp/sandboxed_seven_zip_analyzer_unittest.ccchrome/utility/safe_browsing/archive_analyzer.ccthird_party/lzma_sdk/google/seven_zip_reader_unittest.ccthird_party/lzma_sdk/google/test_data/archive_named_folder.7z
Patch
From 9fd8769735435ca0e9ca2e323e0909bf68bee7d4 Mon Sep 17 00:00:00 2001 From: Andrew Paseltiner <[email protected]> Date: Thu, 23 Jul 2026 12:40:52 -0700 Subject: [PATCH] Don't recurse into directories during archive deep inspection ArchiveAnalyzer::UpdateResultsForEntry() chose a nested analyzer based only on the entry's file extension, so a 7z directory entry whose name has an archive extension (e.g. "folder.zip") spawned a nested analyzer against the temp file. This temp file holds the previous entry's bytes or is empty. SevenZipAnalyzer::OnDirectory() also did not record that nested work was pending, so the overall analysis returned early without inspecting any later entries in the archive. Fixed: 513222422 Change-Id: Icce57e5dc57211aaf98c970d4d40367e7795e547 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8132854 SLSA-Policy-Verified: SLSA Policy Verification Service <[email protected]> Reviewed-by: thefrog <[email protected]> Commit-Queue: Andrew Paseltiner <[email protected]> Cr-Commit-Position: refs/heads/main@{#1667330} --- diff --git a/chrome/services/file_util/public/cpp/sandboxed_seven_zip_analyzer_unittest.cc b/chrome/services/file_util/public/cpp/sandboxed_seven_zip_analyzer_unittest.cc index 1c0037b..ad9ad97 100644 --- a/chrome/services/file_util/public/cpp/sandboxed_seven_zip_analyzer_unittest.cc +++ b/chrome/services/file_util/public/cpp/sandboxed_seven_zip_analyzer_unittest.cc @@ -163,6 +163,35 @@ EXPECT_FALSE(results.archived_binary[2].is_archive()); } +TEST_F(SandboxedSevenZipAnalyzerTest, BinaryAfterArchiveNamedFolder) { + safe_browsing::ArchiveAnalyzerResults results; + RunAnalyzer( + dir_test_data_.Append(FILE_PATH_LITERAL("archive_named_folder.7z")), + &results); + ASSERT_TRUE(results.success); + EXPECT_TRUE(results.has_executable); + EXPECT_TRUE(results.has_archive); + EXPECT_EQ(1, results.file_count); + EXPECT_EQ(1, results.directory_count); + ASSERT_EQ(2, results.archived_binary.size()); + + EXPECT_EQ("folder.zip", results.archived_binary[0].file_path()); + EXPECT_EQ(ClientDownloadRequest::ARCHIVE, + results.archived_binary[0].download_type()); + EXPECT_FALSE(results.archived_binary[0].is_executable()); + EXPECT_TRUE(results.archived_binary[0].is_archive()); + + EXPECT_EQ("file.exe", results.archived_binary[1].file_path()); + EXPECT_EQ(ClientDownloadRequest::WIN_EXECUTABLE, + results.archived_binary[1].download_type()); + EXPECT_EQ("B32E028F9B83C5FFB806CA7DFE7A3ECE5F1AED5A0368B0A140B35A67F5B000B3", + base::HexEncode(results.archived_binary[1].digests().sha256())); + EXPECT_EQ(19, results.archived_binary[1].length()); + EXPECT_FALSE(results.archived_binary[1].is_encrypted()); + EXPECT_TRUE(results.archived_binary[1].is_executable()); + EXPECT_FALSE(results.archived_binary[1].is_archive()); +} + TEST_F(SandboxedSevenZipAnalyzerTest, NestedArchive) { safe_browsing::ArchiveAnalyzerResults results; RunAnalyzer(dir_test_data_.Append(FILE_PATH_LITERAL("inner_archive.7z")), diff --git a/chrome/utility/safe_browsing/archive_analyzer.cc b/chrome/utility/safe_browsing/archive_analyzer.cc index 7aaf094..49e7ae8 100644 --- a/chrome/utility/safe_browsing/archive_analyzer.cc +++ b/chrome/utility/safe_browsing/archive_analyzer.cc @@ -104,7 +104,7 @@ bool is_encrypted, bool is_directory, bool contents_valid) { - if (!is_encrypted) { + if (!is_encrypted && !is_directory) { nested_analyzer_ = ArchiveAnalyzer::CreateForArchiveType(GetFileType(path)); if (nested_analyzer_) { // Archive analyzers expect to start at the beginning of the diff --git a/third_party/lzma_sdk/google/seven_zip_reader_unittest.cc b/third_party/lzma_sdk/google/seven_zip_reader_unittest.cc index 188180a..b291d9a 100644 --- a/third_party/lzma_sdk/google/seven_zip_reader_unittest.cc +++ b/third_party/lzma_sdk/google/seven_zip_reader_unittest.cc @@ -31,6 +31,10 @@ // values with 0. // // echo "This is not an exe" > file.exe +// mkdir folder.zip +// 7z a archive_named_folder.7z file.exe folder.zip +// +// echo "This is not an exe" > file.exe // 7z a -p encrypted.7z file.exe # Provided 1234 as the password // // echo "This is not an exe" > file.exe diff --git a/third_party/lzma_sdk/google/test_data/archive_named_folder.7z b/third_party/lzma_sdk/google/test_data/archive_named_folder.7z new file mode 100644 index 0000000..de4becb9 --- /dev/null +++ b/third_party/lzma_sdk/google/test_data/archive_named_folder.7z Binary files differ
Regression Test / PoC
diff --git a/chrome/services/file_util/public/cpp/sandboxed_seven_zip_analyzer_unittest.cc b/chrome/services/file_util/public/cpp/sandboxed_seven_zip_analyzer_unittest.cc
index 1c0037b..ad9ad97 100644
--- a/chrome/services/file_util/public/cpp/sandboxed_seven_zip_analyzer_unittest.cc
+++ b/chrome/services/file_util/public/cpp/sandboxed_seven_zip_analyzer_unittest.cc
@@ -163,6 +163,35 @@
EXPECT_FALSE(results.archived_binary[2].is_archive());
}
+TEST_F(SandboxedSevenZipAnalyzerTest, BinaryAfterArchiveNamedFolder) {
+ safe_browsing::ArchiveAnalyzerResults results;
+ RunAnalyzer(
+ dir_test_data_.Append(FILE_PATH_LITERAL("archive_named_folder.7z")),
+ &results);
+ ASSERT_TRUE(results.success);
+ EXPECT_TRUE(results.has_executable);
+ EXPECT_TRUE(results.has_archive);
+ EXPECT_EQ(1, results.file_count);
+ EXPECT_EQ(1, results.directory_count);
+ ASSERT_EQ(2, results.archived_binary.size());
+
+ EXPECT_EQ("folder.zip", results.archived_binary[0].file_path());
+ EXPECT_EQ(ClientDownloadRequest::ARCHIVE,
+ results.archived_binary[0].download_type());
+ EXPECT_FALSE(results.archived_binary[0].is_executable());
+ EXPECT_TRUE(results.archived_binary[0].is_archive());
+
+ EXPECT_EQ("file.exe", results.archived_binary[1].file_path());
+ EXPECT_EQ(ClientDownloadRequest::WIN_EXECUTABLE,
+ results.archived_binary[1].download_type());
+ EXPECT_EQ("B32E028F9B83C5FFB806CA7DFE7A3ECE5F1AED5A0368B0A140B35A67F5B000B3",
+ base::HexEncode(results.archived_binary[1].digests().sha256()));
+ EXPECT_EQ(19, results.archived_binary[1].length());
+ EXPECT_FALSE(results.archived_binary[1].is_encrypted());
+ EXPECT_TRUE(results.archived_binary[1].is_executable());
+ EXPECT_FALSE(results.archived_binary[1].is_archive());
+}
+
TEST_F(SandboxedSevenZipAnalyzerTest, NestedArchive) {
safe_browsing::ArchiveAnalyzerResults results;
RunAnalyzer(dir_test_data_.Append(FILE_PATH_LITERAL("inner_archive.7z")),
diff --git a/third_party/lzma_sdk/google/seven_zip_reader_unittest.cc b/third_party/lzma_sdk/google/seven_zip_reader_unittest.cc
index 188180a..b291d9a 100644
--- a/third_party/lzma_sdk/google/seven_zip_reader_unittest.cc
+++ b/third_party/lzma_sdk/google/seven_zip_reader_unittest.cc
@@ -31,6 +31,10 @@
// values with 0.
//
// echo "This is not an exe" > file.exe
+// mkdir folder.zip
+// 7z a archive_named_folder.7z file.exe folder.zip
+//
+// echo "This is not an exe" > file.exe
// 7z a -p encrypted.7z file.exe # Provided 1234 as the password
//
// echo "This is not an exe" > file.exe
Original Bug Report
Safe Browsing deep-scanning bypass in 7z analyzer via directory entries
Project Fortify, an experimental security project, has identified the following potential security issue. If you’re a feature owner CC-ed on this bug, please do your best to review these reports without the Chrome Security team. Please see https://chromium.googlesource.com/chromium/src/+/main/docs/security/ai-generated-security-bugs-faq.md for more information.
Overview: A logic error in the 7z archive analyzer allows specifically crafted archives to bypass Safe Browsing deep-scanning. By including a directory entry with an archive-type extension (e.g., ‘.zip’), an attacker can cause the analysis process to terminate prematurely. This results in subsequent malicious files within the same 7z archive being ignored by the security scanner.
Affected files:
chrome/utility/safe_browsing/seven_zip_analyzer.ccchrome/utility/safe_browsing/archive_analyzer.ccthird_party/lzma_sdk/google/seven_zip_reader.cc
Estimated timestamp from git blame: 2023-04-26
Summary
A potential vulnerability has been identified in the Safe Browsing 7z analyzer where the extraction process can be tricked into finishing early. This allows an attacker to hide malicious binaries within a 7z archive, bypassing hash-blocklist and heuristic checks during deep scanning.
Potential Root Cause
The issue appears to reside in SevenZipAnalyzer::OnDirectory within chrome/utility/safe_browsing/seven_zip_analyzer.cc.
When the analyzer encounters an entry, it calls UpdateResultsForEntry in chrome/utility/safe_browsing/archive_analyzer.cc. This method checks the file extension to decide if it should perform a nested analysis (e.g., scanning a .zip inside the .7z). For a directory named with an archive extension (like folder.zip), it initiates a nested analysis and returns false, signaling that extraction should pause for asynchronous work.
However, in SevenZipAnalyzer::OnDirectory, the code fails to set the awaiting_nested_ flag to true when UpdateResultsForEntry returns false:
// chrome/utility/safe_browsing/seven_zip_analyzer.cc
bool SevenZipAnalyzer::OnDirectory(const seven_zip::EntryInfo& entry) {
return UpdateResultsForEntry(
temp_file_.Duplicate(), GetRootPath().Append(entry.file_path),
entry.file_size, entry.is_encrypted, /*is_directory=*/true,
/*contents_valid=*/!entry.is_encrypted);
} // BUG: awaiting_nested_ is never set to true here.
When this returns false to the underlying SevenZipReader, the extraction loop breaks. Control returns to SevenZipAnalyzer::ResumeExtraction(), which checks awaiting_nested_. Because the flag is false, it returns true, indicating completion to the parent ArchiveAnalyzer. This causes the analyzer to report final results to the browser process prematurely, before subsequent entries in the 7z archive have been inspected.
Furthermore, if the nested analysis eventually finishes, it may trigger a CHECK failure in the utility process because the completion callback (finished_analysis_callback_) has already been consumed and nullified during the premature completion.
Potential Steps to Reproduce
- Create a 7z archive where the first entry is a directory named
bypass.zip. - Add a known-malicious executable (e.g., a test EICAR file or
malware.exe) as the second entry in the archive. - Download the archive using a Chrome instance with Safe Browsing ‘Enhanced Protection’ or Deep Scanning enabled.
- Observe the Safe Browsing logs (e.g., via
chrome://safe-browsing) to see if the analysis report includes the malicious executable. If the bypass is successful, the report will likely show a valid archive with no identified binaries.
Suggested Fix
In chrome/utility/safe_browsing/seven_zip_analyzer.cc, update OnDirectory to correctly set the awaiting_nested_ flag when UpdateResultsForEntry returns false, consistent with how it is handled in EntryDone:
bool SevenZipAnalyzer::OnDirectory(const seven_zip::EntryInfo& entry) {
if (!UpdateResultsForEntry(
temp_file_.Duplicate(), GetRootPath().Append(entry.file_path),
entry.file_size, entry.is_encrypted, /*is_directory=*/true,
/*contents_valid=*/!entry.is_encrypted)) {
awaiting_nested_ = true;
return false;
}
return true;
}
Evaluated with Chrome root at commit: b3153093eb3c78c3e88ccf562bcbc20437a04b0e
Results so far have been promising, but there can be wrong deductions. Feel free to adjust as follows:
- If you are familiar with the severity guidelines, you may adjust the severity.
- If this is a false positive, and there’s no work to be done, please close as WAI.
- If there is work to do here but not a vulnerability, please change the issue type to Task/Bug/FR.
Data from false positives will be used to improve accuracy over time. And please feel free to reach out to me directly if you have concerns or feedback on the project.