CVE-2026-87548
Overview
Changed Functions
| Function | Change | Notes |
|---|---|---|
TEST_Fchrome/services/file_util/public/cpp/sandboxed_seven_zip_analyzer_unittest.cc |
modified |
Files Changed
chrome/services/file_util/public/cpp/sandboxed_seven_zip_analyzer_unittest.ccchrome/utility/safe_browsing/seven_zip_analyzer.ccthird_party/lzma_sdk/google/test_data/unsupported_coder_chain.7z
Patch
From 9f4f461aaf77809973b4f4e42de7b090ae8deb9b Mon Sep 17 00:00:00 2001
From: thefrog <thefrog@chromium.org>
Date: Fri, 31 Jul 2026 14:41:25 -0700
Subject: [PATCH] Fix 7z unpacking error incorrect reported metadata in download ping
When an unencrypted 7z archive entry failed to unpack,
SevenZipAnalyzer::EntryDone() previously incorrectly marked the archive
result as valid.
As a result, the download ping would incorrectly contain:
- archive_summary.parser_status = "VALID"
- download_type = "ARCHIVE"
This change updates SevenZipAnalyzer::EntryDone() so that if an
unencrypted entry fails to unpack, the archive analysis is marked as
failed.
The download ping now contains:
- archive_summary.parser_status is not set
- download_type = "INVALID_SEVEN_ZIP"
Reviewed in https://crrev.com/i/9620201.
Fixed: 513495219
Change-Id: I6efa84b5ec9fdfe1ca34fc1aef9de002d1ccf6f8
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8183009
Auto-Submit: thefrog <thefrog@chromium.org>
Commit-Queue: Yaw Frempong <yawfrempong@google.com>
Commit-Queue: thefrog <thefrog@chromium.org>
Reviewed-by: Yaw Frempong <yawfrempong@google.com>
SLSA-Policy-Verified: SLSA Policy Verification Service <devtools-gerritcodereview-exitgate@google.com>
Cr-Commit-Position: refs/heads/main@{#1672117}
---
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 ad9ad97..86a2771 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
@@ -235,6 +235,18 @@
EXPECT_FALSE(results.archived_binary[0].is_archive());
}
+TEST_F(SandboxedSevenZipAnalyzerTest, UnsupportedCoderChainFailsAnalysis) {
+ safe_browsing::ArchiveAnalyzerResults results;
+ RunAnalyzer(
+ dir_test_data_.Append(FILE_PATH_LITERAL("unsupported_coder_chain.7z")),
+ &results);
+ EXPECT_FALSE(results.success);
+ EXPECT_EQ(ArchiveAnalysisResult::kFailedDuringIteration,
+ results.analysis_result);
+ EXPECT_FALSE(results.encryption_info.is_encrypted);
+ EXPECT_EQ(0, results.archived_binary.size());
+}
+
TEST_F(SandboxedSevenZipAnalyzerTest, NotASevenZip) {
safe_browsing::ArchiveAnalyzerResults results;
RunAnalyzer(dir_test_data_.Append(FILE_PATH_LITERAL("not_a_seven_zip.7z")),
diff --git a/chrome/utility/safe_browsing/seven_zip_analyzer.cc b/chrome/utility/safe_browsing/seven_zip_analyzer.cc
index 259392e28..766668f 100644
--- a/chrome/utility/safe_browsing/seven_zip_analyzer.cc
+++ b/chrome/utility/safe_browsing/seven_zip_analyzer.cc
@@ -93,6 +93,11 @@
awaiting_nested_ = true;
return false;
}
+ } else {
+ // If an unencrypted entry failed to unpack, mark the entire archive
+ // analysis as failed so we do not report it as a valid archive.
+ results()->success = false;
+ results()->analysis_result = ArchiveAnalysisResult::kFailedDuringIteration;
}
return true;
diff --git a/third_party/lzma_sdk/google/test_data/unsupported_coder_chain.7z b/third_party/lzma_sdk/google/test_data/unsupported_coder_chain.7z
new file mode 100644
index 0000000..589cbc3
--- /dev/null
+++ b/third_party/lzma_sdk/google/test_data/unsupported_coder_chain.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 ad9ad97..86a2771 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
@@ -235,6 +235,18 @@
EXPECT_FALSE(results.archived_binary[0].is_archive());
}
+TEST_F(SandboxedSevenZipAnalyzerTest, UnsupportedCoderChainFailsAnalysis) {
+ safe_browsing::ArchiveAnalyzerResults results;
+ RunAnalyzer(
+ dir_test_data_.Append(FILE_PATH_LITERAL("unsupported_coder_chain.7z")),
+ &results);
+ EXPECT_FALSE(results.success);
+ EXPECT_EQ(ArchiveAnalysisResult::kFailedDuringIteration,
+ results.analysis_result);
+ EXPECT_FALSE(results.encryption_info.is_encrypted);
+ EXPECT_EQ(0, results.archived_binary.size());
+}
+
TEST_F(SandboxedSevenZipAnalyzerTest, NotASevenZip) {
safe_browsing::ArchiveAnalyzerResults results;
RunAnalyzer(dir_test_data_.Append(FILE_PATH_LITERAL("not_a_seven_zip.7z")),
Original Bug Report
Safe Browsing bypass in 7z analysis due to ignored error and coder limit discrepancy
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. 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 reader ignores errors during encryption detection, causing incorrect metadata reporting. Combined with a coder limit discrepancy, this allows crafted archives to bypass Safe Browsing analysis. Malicious payloads can be hidden from deep-scanning while remaining accessible to users.
Affected files:
third_party/lzma_sdk/google/seven_zip_reader.ccchrome/utility/safe_browsing/seven_zip_analyzer.cc
Estimated timestamp from git blame: 2022-09-20
Summary
A potential vulnerability exists in Chromium’s Safe Browsing 7z archive analysis where an ignored return value in SevenZipReaderImpl::IsFolderEncrypted leads to incorrect metadata reporting. When combined with a ‘parser differential’—where the initial archive scan permits more coders than the subsequent extraction and metadata pass—an attacker can craft a 7z archive that Safe Browsing fails to correctly identify as containing encrypted or executable content. This may allow malicious payloads to bypass deep-scanning signals.
Root Cause Analysis
The issue resides in SevenZipReaderImpl::IsFolderEncrypted within third_party/lzma_sdk/google/seven_zip_reader.cc. This function calls SzGetNextFolderItem to parse a folder’s coder graph but ignores its SRes return value:
// third_party/lzma_sdk/google/seven_zip_reader.cc
CSzFolder folder;
SzGetNextFolderItem(&folder, &span); // <-- SRes return value IGNORED
for (size_t i = 0; i < folder.NumCoders && i < SZ_NUM_CODERS_IN_FOLDER_MAX; ++i) {
// ...
}
In the LZMA SDK, SzGetNextFolderItem initializes f->NumCoders = 0 and only sets the actual value after all validation passes. If validation fails (e.g., returning SZ_ERROR_UNSUPPORTED), NumCoders remains 0. Consequently, the loop in IsFolderEncrypted never runs, and the function incorrectly returns false regardless of the actual folder contents.
Parser Differential
A discrepancy exists between the archive scanning pass (ReadUnpackInfo called during SzArEx_Open) and the decoding/metadata pass (SzGetNextFolderItem).
- The initial scanning pass in the LZMA SDK allows up to 64 coders (
k_Scan_NumCoders_MAX). - The decoding pass and metadata parsing used by Chrome’s reader limit this to 4 coders (
SZ_NUM_CODERS_IN_FOLDER_MAX).
An archive folder utilizing 5 coders will be accepted during the initial SzArEx_Open call but will cause SzGetNextFolderItem to fail during the subsequent metadata-collection and extraction phases.
Potential Exploitation Steps
An attacker might follow these steps to potentially trigger the vulnerability:
- Craft a
.7zfile where a payload folder uses 5 coders, including an encryption method (e.g., 7zAES). - When a user downloads the file,
SevenZipAnalyzerinitiates a scan in the sandboxed utility process. SzArEx_Opensucceeds because its scanning pass allows up to 64 coders.IsFolderEncryptedis called for the payload entry.SzGetNextFolderItemfails due to the 4-coder limit and returnsSZ_ERROR_UNSUPPORTED. The error is ignored, andentry.is_encryptedis incorrectly set tofalse(sincefolder.NumCoderswas reset to 0).ExtractFilesubsequently fails duringSzAr_DecodeFolderfor the same reason (the 4-coder limit), returningResult::kUnsupported.- In
SevenZipAnalyzer::EntryDone, the following logic is applied:Sinceif (result == seven_zip::Result::kSuccess || entry.is_encrypted) { ... }resultiskUnsupportedandentry.is_encryptedisfalse, the analyzer silently drops the entry.
The resulting Safe Browsing report sent to the backend will omit the malicious payload’s metadata (filename, size, and executable status). This suppresses potential warnings like ZIPPED_EXECUTABLE, while the malicious payload remains accessible to the user via standard desktop 7z applications that support more than 4 coders.
Impact
Successful exploitation allows malicious files to bypass Safe Browsing analysis, reducing the effectiveness of download protection. The attacker can hide executable content or encrypted payloads from the analysis infrastructure.
Suggested Fix
- Modify
SevenZipReaderImpl::IsFolderEncryptedinthird_party/lzma_sdk/google/seven_zip_reader.ccto check and handle the return value ofSzGetNextFolderItem. - Consider aligning the coder limits between the scanning pass and the decoding pass in the LZMA SDK integration to prevent parser differentials. Increasing
SZ_NUM_CODERS_IN_FOLDER_MAXto matchk_Scan_NumCoders_MAX(within safe limits) or ensuring consistent enforcement across all passes would mitigate the issue.
Evaluated with Chrome root at commit: 1a8d40fc44df2088d5945c0bf53584038aa1614a
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.