Coordinated Disclosure Timeline
- 2025-04-24: Reported as a private issue
- 2025-04-29: Report acknowledged
- 2025-07-05: Fixed in v25.00
Summary
Zeroes written outside heap buffer in RAR5 handler may lead to memory corruption and denial of service.
Project
7-Zip
Tested Version
Details
Multi byte write heap buffer overflow in NCompress::NRar5::CDecoder
(GHSL-2025-058
)
RAR5 decoder attempts to fix corrupted items by filling them with zeroes. However a miscalculation [2] of the rem
value in My_ZeroMemory(_window + _winPos, (size_t)rem);
[1] call leads to zeroes written past the allocated buffer.
Z7_COM7F_IMF(CDecoder::Code(ISequentialInStream *inStream, ISequentialOutStream *outStream,
const UInt64 * /* inSize */, const UInt64 *outSize, ICompressProgressInfo *progress))
{
...
#define Z7_RAR_RECOVER_SOLID_LIMIT (1 << 20)
...
{
const UInt64 lzSize = _lzSize + _winPos;
...
#if Z7_RAR_RECOVER_SOLID_LIMIT != 0
else if (lzSize < _lzEnd)
{
...
// we can report that recovering was made:
// _lzError = LZ_ERROR_TYPE_HEADER;
// We write zeros to area after corruption:
if (_window)
{
UInt64 rem = _lzEnd - lzSize; // <------- 2
const size_t ws = _winSize;
if (rem >= ws)
{
My_ZeroMemory(_window, ws);
_lzSize = ws;
_winPos = 0;
}
else
{
const size_t cur = ws - _winPos;
if (cur <= rem)
{
rem -= cur;
My_ZeroMemory(_window + _winPos, cur);
_lzSize += _winPos;
_winPos = 0;
}
My_ZeroMemory(_window + _winPos, (size_t)rem); // <-------- 1
_winPos += (size_t)rem;
}
}
...
}
#endif
}
...
_unpackSize = 0;
_unpackSize_Defined = (outSize != NULL);
if (_unpackSize_Defined)
_unpackSize = *outSize;
if ((Int64)_unpackSize >= 0)
_lzEnd += _unpackSize; // known end after current file // <------------- 3
else
_lzEnd = 0; // unknown end
...
}
PoC triggers heap buffer write overflow when 7zz
is compiled with ASAN and extracted, for example as 7zz e -so rar-crash.rar5
:
==2188082==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x7fc75fbcc844 at pc 0x5567af835070 bp 0x7fff7f71ce30 sp 0x7fff7f71c600
WRITE of size 9469 at 0x7fc75fbcc844 thread T0
#0 0x5567af83506f in __asan_memset /src/llvm-project/compiler-rt/lib/asan/asan_interceptors_memintrinsics.cpp:67:3
#1 0x5567b0167b0c in My_ZeroMemory(void*, unsigned long) /src/7-zip/CPP/7zip/Bundles/Alone2/../../Compress/Rar5Decoder.cpp:63:5
#2 0x5567b017c257 in NCompress::NRar5::CDecoder::Code(ISequentialInStream*, ISequentialOutStream*, unsigned long const*, unsigned long const*, ICompressProgressInfo*) /src/7-zip/CPP/7zip/Bundles/Alone2/../../Compress/Rar5Decoder.cpp:1905:11
#3 0x5567aff075c0 in NArchive::NRar5::CUnpacker::Code(NArchive::NRar5::CItem const&, NArchive::NRar5::CItem const&, unsigned long, ISequentialInStream*, ISequentialOutStream*, ICompressProgressInfo*, bool&) /src/7-zip/CPP/7zip/Bundles/Alone2/../../Archive/Rar/Rar5Handler.cpp:1165:24
#4 0x5567aff24721 in NArchive::NRar5::CHandler::Extract(unsigned int const*, unsigned int, int, IArchiveExtractCallback*) /src/7-zip/CPP/7zip/Bundles/Alone2/../../Archive/Rar/Rar5Handler.cpp:3293:25
#5 0x5567b0244c0b in DecompressArchive(CCodecs*, CArchiveLink const&, unsigned long, NWildcard::CCensorNode const&, CExtractOptions const&, bool, IExtractCallbackUI*, IFolderArchiveExtractCallback*, CArchiveExtractCallback*, UString&, unsigned long&) /src/7-zip/CPP/7zip/Bundles/Alone2/../../UI/Common/Extract.cpp:235:23
#6 0x5567b023fe41 in Extract(CCodecs*, CObjectVector<COpenType> const&, CRecordVector<int> const&, CObjectVector<UString>&, CObjectVector<UString>&, NWildcard::CCensorNode const&, CExtractOptions const&, IOpenCallbackUI*, IExtractCallbackUI*, IFolderArchiveExtractCallback*, IHashCalc*, UString&, CDecompressStat&) /src/7-zip/CPP/7zip/Bundles/Alone2/../../UI/Common/Extract.cpp:542:5
#7 0x5567b02f9d8a in Main2(int, char**) /src/7-zip/CPP/7zip/Bundles/Alone2/../../UI/Console/Main.cpp:1378:21
#8 0x5567b0305b34 in main /src/7-zip/CPP/7zip/Bundles/Alone2/../../UI/Console/MainAr.cpp:162:11
On Windows the same PoC was tested to crash the official 7-Zip build even without ASAN.
Impact
The bytes past the allocated heap buffer are always overwritten with zeroes: My_ZeroMemory(_window + _winPos, (size_t)rem)
[1], but rem
is calculated as UInt64 rem = _lzEnd - lzSize;
[2] where _lzEnd
depends on the size of the previous item in archive which is attacker controlled: _lzEnd += _unpackSize
[3]. Thus the attacker may control how many bytes to overwrite.
It is unlikely it could lead to arbitrary code execution, but it may lead to denial of service because of the memory corruption.
CWEs
- CWE-122: Heap-based Buffer Overflow
CVE
- CVE-2025-53816 - GHSL-2025-058
Credit
This issue was discovered and reported by GHSL team member @JarLob (Jaroslav Lobačevski).
Contact
You can contact the GHSL team at securitylab@github.com
, please include a reference to GHSL-2025-058
in any communication regarding this issue.