Feb 28, 2026·5 min read·4 visits
Logic error in malcontent < 1.21.0 deletes nested archives even if extraction fails, allowing malformed malicious files to bypass scanning.
A logic vulnerability in the `malcontent` supply chain security tool allows attackers to bypass detection engines. The issue resides in the handling of nested archives (e.g., a `.tar` inside a `.zip`). When the scanner attempts to extract a nested archive and fails due to malformed headers or corruption, it unconditionally deletes the source archive file. This removal occurs before the content scanning phase, effectively hiding the malicious payload from analysis. Attackers can exploit this by crafting archives that fail extraction but contain malicious signatures in their raw bytes.
malcontent is an open-source security tool developed by Chainguard to detect supply-chain compromises. It employs a combination of context analysis, differential analysis, and YARA signatures to identify malicious code within software artifacts. A core feature of the tool is its ability to recursively unpack nested archives to inspect their contents.
The vulnerability, identified as CVE-2026-28407, affects the extraction logic for these nested archives. In affected versions (< 1.21.0), the scanner enforces a cleanup policy that is too aggressive. When malcontent encounters a nested archive, it attempts to extract it to a temporary directory for analysis. Crucially, the code was designed to remove the temporary archive file after the extraction attempt to save space. However, this removal was performed unconditionally, regardless of whether the extraction succeeded or failed.
This behavior creates a race-condition-like logic flaw where the artifact containing potential malware is deleted from the disk specifically because it failed to unpack. Since the subsequent scanning phases (YARA and heuristics) operate on the files remaining on the disk, the malicious artifact simply vanishes from the scanner's view, resulting in a false negative report.
The root cause is an improper handling of exceptional conditions (CWE-703) within the extractNestedArchive function in pkg/archive/archive.go (and related extraction logic). The function is responsible for orchestrating the decompression of files found within other files.
The logic flow in vulnerable versions was as follows:
tar, zip, gzip).os.Remove(archivePath) to delete the nested archive file.By deleting the file even after a failure, the code assumes that if extraction failed, the file is either useless or benign garbage. This assumption is incorrect in a security context. A file can be a valid executable or contain a valid YARA signature while simultaneously having a corrupted archive header that causes standard extraction libraries to error out. The scanner relies on inspecting the extracted content OR the raw content. By deleting the raw content after failing to produce extracted content, the scanner inspects nothing.
The vulnerability is evident in the patch diff provided by the maintainers. The fix involves wrapping the deletion logic in a conditional block that checks if the extraction error was nil.
In the original code, the cleanup os.Remove runs regardless of the err state from the extraction function.
// ... inside the extraction loop ...
err := extract(f, d)
if err != nil {
// Error is logged, but control flow continues
logger.Debugf("ignoring extraction error for %s: %s", f, err.Error())
}
// CRITICAL FLAW: The source file 'fullPath' is deleted even if extraction failed.
// This removes the evidence before the scanner can look at it.
if err := os.Remove(fullPath); err != nil {
return fmt.Errorf("failed to remove archive file: %w", err)
}The fix introduced in version 1.21.0 explicitly preserves the file if extraction fails. This allows the scanner to fall back to scanning the raw file itself.
// ... inside the extraction loop ...
err := extract(f, d)
// ... error logging ...
// FIX: Only attempt to remove the archive file if we successfully extracted it.
// If 'err' is not nil, the file remains on disk for the scanner to inspect.
if err == nil {
if err := os.Remove(fullPath); err != nil {
return fmt.Errorf("failed to remove archive file: %w", err)
}
}Exploiting this vulnerability requires an attacker to craft a "broken" archive that is technically invalid enough to fail the specific Go extraction library used by malcontent, yet still contains the malicious payload in its byte stream.
payload.bin is put inside inner.tar.gz.inner.tar.gz or truncates the footer. The goal is to ensure that gzip or tar readers return an error (e.g., unexpected EOF or invalid header) immediately upon processing, or partially through processing.inner.tar.gz is packed inside a valid outer.zip.When malcontent scans outer.zip, it successfully extracts inner.tar.gz. It then identifies inner.tar.gz as an archive and attempts to extract it. The extraction fails due to the corruption. The vulnerable code catches the error, logs "ignoring extraction error", and immediately deletes inner.tar.gz.
The scanner then iterates over the directory. Since inner.tar.gz was deleted and no files were extracted from it, the directory appears empty or benign. The malicious payload, which existed in the raw bytes of inner.tar.gz, is gone.
The primary impact of CVE-2026-28407 is a Security Control Bypass. malcontent is relied upon to gate software supply chains, often running in CI/CD pipelines to block malicious dependencies or artifacts.
By successfully bypassing this check, an attacker can introduce malware into a trusted environment. While the vulnerability does not directly grant remote code execution (RCE) on the scanning infrastructure itself, it neutralizes the scanner's ability to protect downstream systems.
Severity Metrics:
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N. The attack vector is Network, and complexity is Low. The impact is framed as Low Integrity impact because the integrity of the scan results is compromised.malcontent version < 1.21.0 for artifact validation.CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N| Product | Affected Versions | Fixed Version |
|---|---|---|
malcontent chainguard-dev | < 1.21.0 | 1.21.0 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-703 |
| CVSS v4.0 | 6.9 (Medium) |
| Attack Vector | Network |
| Vendor | Chainguard |
| Exploit Status | POC Available |
| Patch Status | Released (v1.21.0) |
Improper Check or Handling of Exceptional Conditions