CVE-2016-4300
An exploitable \heap overflow vulnerability exists in the 7zip read_SubStreamsInfo functionality of libarchive. A specially crafted 7zip file can cause a integer overflow resulting in memory corruption that can lead to code execution. An attacker can send a malformed file to trigger this vulnerability.
libarchive 3.1.2
https://github.com/libarchive/libarchive
7.8 - CVSS:3.0/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
Vulnerable code exists in 7zip support format module: libarchive\archive_read_support_format_7zip.c:
(...)
#define UMAX_ENTRY ARCHIVE_LITERAL_ULL(100000000)
(...)
Line 2129 static int
Line 2130 read_SubStreamsInfo(struct archive_read *a, struct _7z_substream_info *ss,
Line 2131 struct _7z_folder *f, size_t numFolders)
Line 2132 {
Line 2133 const unsigned char *p;
Line 2134 uint64_t *usizes;
Line 2135 size_t unpack_streams;
Line 2136 int type;
Line 2137 unsigned i;
Line 2138 uint32_t numDigests;
(...)
Line 2149 if (type == kNumUnPackStream) {
Line 2150 unpack_streams = 0;
Line 2151 for (i = 0; i < numFolders; i++) {
Line 2152 if (parse_7zip_uint64(a, &(f[i].numUnpackStreams)) < 0)
Line 2153 return (-1);
Line 2154 if (UMAX_ENTRY < f[i].numUnpackStreams)
Line 2155 return (-1);
Line 2156 unpack_streams += (size_t)f[i].numUnpackStreams; // <---- INTEGER OVERFLOW
Line 2157 }
Line 2158 if ((p = header_bytes(a, 1)) == NULL)
Line 2159 return (-1);
Line 2160 type = *p;
Line 2161 } else
Line 2162 unpack_streams = numFolders;
Line 2163
Line 2164 ss->unpack_streams = unpack_streams;
Line 2165 if (unpack_streams) {
Line 2166 ss->unpackSizes = calloc(unpack_streams, // <----- ALLOCATION BASED ON OVERFLOWED INT
Line 2167 sizeof(*ss->unpackSizes));
Line 2168 ss->digestsDefined = calloc(unpack_streams,
Line 2169 sizeof(*ss->digestsDefined));
Line 2170 ss->digests = calloc(unpack_streams,
Line 2171 sizeof(*ss->digests));
Line 2172 if (ss->unpackSizes == NULL || ss->digestsDefined == NULL ||
Line 2173 ss->digests == NULL)
Line 2174 return (-1);
Line 2175 }
Line 2176
Line 2177 usizes = ss->unpackSizes;
Line 2178 for (i = 0; i < numFolders; i++) {
Line 2179 unsigned pack;
Line 2180 uint64_t sum;
Line 2181
Line 2182 if (f[i].numUnpackStreams == 0)
Line 2183 continue;
Line 2184
Line 2185 sum = 0;
Line 2186 if (type == kSize) {
Line 2187 for (pack = 1; pack < f[i].numUnpackStreams; pack++) {
Line 2188 if (parse_7zip_uint64(a, usizes) < 0) // <--- BUFFER OVERFLOW
Line 2189 return (-1);
Line 2190 sum += *usizes++;
Line 2191 }
Line 2192 }
Line 2193 *usizes++ = folder_uncompressed_size(&f[i]) - sum;
Line 2194 }
In lines 2149-2157 we can see that for all “folders” is calculated sum of “numUnpackStreams” which result is stored in “unpack_streams” variable. This variable is size_t what means that on x86 platform will be 32bit unsigned int. Maxium value of of “numUnpackStreams” is equal to UMAX_ENTRY. To overflow “unpack_streams” variable we need to have 7zip file with number of folders “numFolders” bigger than 42 and “numUnpackStreams” with sufficient values.
The overflowed value is also used as size parameter in calloc in lines 2166-2171. Interesting buffer allocated there for us is “ss->unpackSizes”. Later in lines 2187-2194 based on “numFolders” and “numUnpackStreams” of each folder, a 64bit unsigned integer (maximum) is read from the file and stored into buffer “usizes” which is indeed “ss->unpackSizes” ( line 2177 ) causing a heap based buffer overflow after some iterations. At this point the attacker controls the length and contents of the memory corruption.
valgrind output:
==33167== Memcheck, a memory error detector
==33167== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==33167== Using Valgrind-3.10.0.SVN and LibVEX; rerun with -h for copyright info
==33167==
==33167== Invalid write of size 4
==33167== at 0x805A459: parse_7zip_uint64 (archive_read_support_format_7zip.c:1666)
==33167== by 0x805B9C0: read_SubStreamsInfo (archive_read_support_format_7zip.c:2191)
==33167== by 0x805BF42: read_StreamsInfo (archive_read_support_format_7zip.c:2320)
==33167== by 0x805C0ED: read_Header (archive_read_support_format_7zip.c:2390)
==33167== by 0x805D5DE: slurp_central_directory (archive_read_support_format_7zip.c:2927)
==33167== by 0x805834F: archive_read_format_7zip_read_header (archive_read_support_format_7zip.c:640)
==33167== by 0x804E3C6: _archive_read_next_header2 (archive_read.c:645)
==33167== by 0x804E47A: _archive_read_next_header (archive_read.c:683)
==33167== by 0x8088A06: archive_read_next_header (archive_virtual.c:148)
==33167== by 0x804A178: extract (ext.c:55)
==33167== by 0x804A271: main (ext.c:83)
==33167== Address 0x445cd28 is 0 bytes after a block of size 8 alloc'd
==33167== at 0x402C109: calloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==33167== by 0x805B8FA: read_SubStreamsInfo (archive_read_support_format_7zip.c:2169)
==33167== by 0x805BF42: read_StreamsInfo (archive_read_support_format_7zip.c:2320)
==33167== by 0x805C0ED: read_Header (archive_read_support_format_7zip.c:2390)
==33167== by 0x805D5DE: slurp_central_directory (archive_read_support_format_7zip.c:2927)
==33167== by 0x805834F: archive_read_format_7zip_read_header (archive_read_support_format_7zip.c:640)
==33167== by 0x804E3C6: _archive_read_next_header2 (archive_read.c:645)
==33167== by 0x804E47A: _archive_read_next_header (archive_read.c:683)
==33167== by 0x8088A06: archive_read_next_header (archive_virtual.c:148)
==33167== by 0x804A178: extract (ext.c:55)
==33167== by 0x804A271: main (ext.c:83)
==33167==
==33167== Invalid write of size 4
==33167== at 0x805A45F: parse_7zip_uint64 (archive_read_support_format_7zip.c:1666)
==33167== by 0x805B9C0: read_SubStreamsInfo (archive_read_support_format_7zip.c:2191)
==33167== by 0x805BF42: read_StreamsInfo (archive_read_support_format_7zip.c:2320)
==33167== by 0x805C0ED: read_Header (archive_read_support_format_7zip.c:2390)
==33167== by 0x805D5DE: slurp_central_directory (archive_read_support_format_7zip.c:2927)
==33167== by 0x805834F: archive_read_format_7zip_read_header (archive_read_support_format_7zip.c:640)
==33167== by 0x804E3C6: _archive_read_next_header2 (archive_read.c:645)
==33167== by 0x804E47A: _archive_read_next_header (archive_read.c:683)
==33167== by 0x8088A06: archive_read_next_header (archive_virtual.c:148)
==33167== by 0x804A178: extract (ext.c:55)
==33167== by 0x804A271: main (ext.c:83)
==33167== Address 0x445cd2c is 4 bytes after a block of size 8 alloc'd
==33167== at 0x402C109: calloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==33167== by 0x805B8FA: read_SubStreamsInfo (archive_read_support_format_7zip.c:2169)
==33167== by 0x805BF42: read_StreamsInfo (archive_read_support_format_7zip.c:2320)
==33167== by 0x805C0ED: read_Header (archive_read_support_format_7zip.c:2390)
==33167== by 0x805D5DE: slurp_central_directory (archive_read_support_format_7zip.c:2927)
==33167== by 0x805834F: archive_read_format_7zip_read_header (archive_read_support_format_7zip.c:640)
==33167== by 0x804E3C6: _archive_read_next_header2 (archive_read.c:645)
==33167== by 0x804E47A: _archive_read_next_header (archive_read.c:683)
==33167== by 0x8088A06: archive_read_next_header (archive_virtual.c:148)
==33167== by 0x804A178: extract (ext.c:55)
==33167== by 0x804A271: main (ext.c:83)
==33167==
==33167== Invalid read of size 4
==33167== at 0x805A512: parse_7zip_uint64 (archive_read_support_format_7zip.c:1675)
==33167== by 0x805B9C0: read_SubStreamsInfo (archive_read_support_format_7zip.c:2191)
==33167== by 0x805BF42: read_StreamsInfo (archive_read_support_format_7zip.c:2320)
==33167== by 0x805C0ED: read_Header (archive_read_support_format_7zip.c:2390)
==33167== by 0x805D5DE: slurp_central_directory (archive_read_support_format_7zip.c:2927)
==33167== by 0x805834F: archive_read_format_7zip_read_header (archive_read_support_format_7zip.c:640)
==33167== by 0x804E3C6: _archive_read_next_header2 (archive_read.c:645)
==33167== by 0x804E47A: _archive_read_next_header (archive_read.c:683)
==33167== by 0x8088A06: archive_read_next_header (archive_virtual.c:148)
==33167== by 0x804A178: extract (ext.c:55)
==33167== by 0x804A271: main (ext.c:83)
==33167== Address 0x445cd28 is 0 bytes after a block of size 8 alloc'd
==33167== at 0x402C109: calloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==33167== by 0x805B8FA: read_SubStreamsInfo (archive_read_support_format_7zip.c:2169)
==33167== by 0x805BF42: read_StreamsInfo (archive_read_support_format_7zip.c:2320)
==33167== by 0x805C0ED: read_Header (archive_read_support_format_7zip.c:2390)
==33167== by 0x805D5DE: slurp_central_directory (archive_read_support_format_7zip.c:2927)
==33167== by 0x805834F: archive_read_format_7zip_read_header (archive_read_support_format_7zip.c:640)
==33167== by 0x804E3C6: _archive_read_next_header2 (archive_read.c:645)
==33167== by 0x804E47A: _archive_read_next_header (archive_read.c:683)
==33167== by 0x8088A06: archive_read_next_header (archive_virtual.c:148)
==33167== by 0x804A178: extract (ext.c:55)
==33167== by 0x804A271: main (ext.c:83)
==33167==
==33167== Invalid read of size 4
==33167== at 0x805A514: parse_7zip_uint64 (archive_read_support_format_7zip.c:1675)
==33167== by 0x805B9C0: read_SubStreamsInfo (archive_read_support_format_7zip.c:2191)
==33167== by 0x805BF42: read_StreamsInfo (archive_read_support_format_7zip.c:2320)
==33167== by 0x805C0ED: read_Header (archive_read_support_format_7zip.c:2390)
==33167== by 0x805D5DE: slurp_central_directory (archive_read_support_format_7zip.c:2927)
==33167== by 0x805834F: archive_read_format_7zip_read_header (archive_read_support_format_7zip.c:640)
==33167== by 0x804E3C6: _archive_read_next_header2 (archive_read.c:645)
==33167== by 0x804E47A: _archive_read_next_header (archive_read.c:683)
==33167== by 0x8088A06: archive_read_next_header (archive_virtual.c:148)
==33167== by 0x804A178: extract (ext.c:55)
==33167== by 0x804A271: main (ext.c:83)
==33167== Address 0x445cd2c is 4 bytes after a block of size 8 alloc'd
==33167== at 0x402C109: calloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==33167== by 0x805B8FA: read_SubStreamsInfo (archive_read_support_format_7zip.c:2169)
==33167== by 0x805BF42: read_StreamsInfo (archive_read_support_format_7zip.c:2320)
==33167== by 0x805C0ED: read_Header (archive_read_support_format_7zip.c:2390)
==33167== by 0x805D5DE: slurp_central_directory (archive_read_support_format_7zip.c:2927)
==33167== by 0x805834F: archive_read_format_7zip_read_header (archive_read_support_format_7zip.c:640)
==33167== by 0x804E3C6: _archive_read_next_header2 (archive_read.c:645)
==33167== by 0x804E47A: _archive_read_next_header (archive_read.c:683)
==33167== by 0x8088A06: archive_read_next_header (archive_virtual.c:148)
==33167== by 0x804A178: extract (ext.c:55)
==33167== by 0x804A271: main (ext.c:83)
==33167==
==33167== Invalid write of size 4
==33167== at 0x805A540: parse_7zip_uint64 (archive_read_support_format_7zip.c:1675)
==33167== by 0x805B9C0: read_SubStreamsInfo (archive_read_support_format_7zip.c:2191)
==33167== by 0x805BF42: read_StreamsInfo (archive_read_support_format_7zip.c:2320)
==33167== by 0x805C0ED: read_Header (archive_read_support_format_7zip.c:2390)
==33167== by 0x805D5DE: slurp_central_directory (archive_read_support_format_7zip.c:2927)
==33167== by 0x805834F: archive_read_format_7zip_read_header (archive_read_support_format_7zip.c:640)
==33167== by 0x804E3C6: _archive_read_next_header2 (archive_read.c:645)
==33167== by 0x804E47A: _archive_read_next_header (archive_read.c:683)
==33167== by 0x8088A06: archive_read_next_header (archive_virtual.c:148)
==33167== by 0x804A178: extract (ext.c:55)
==33167== by 0x804A271: main (ext.c:83)
==33167== Address 0x445cd28 is 0 bytes after a block of size 8 alloc'd
==33167== at 0x402C109: calloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==33167== by 0x805B8FA: read_SubStreamsInfo (archive_read_support_format_7zip.c:2169)
==33167== by 0x805BF42: read_StreamsInfo (archive_read_support_format_7zip.c:2320)
==33167== by 0x805C0ED: read_Header (archive_read_support_format_7zip.c:2390)
==33167== by 0x805D5DE: slurp_central_directory (archive_read_support_format_7zip.c:2927)
==33167== by 0x805834F: archive_read_format_7zip_read_header (archive_read_support_format_7zip.c:640)
==33167== by 0x804E3C6: _archive_read_next_header2 (archive_read.c:645)
==33167== by 0x804E47A: _archive_read_next_header (archive_read.c:683)
==33167== by 0x8088A06: archive_read_next_header (archive_virtual.c:148)
==33167== by 0x804A178: extract (ext.c:55)
==33167== by 0x804A271: main (ext.c:83)
==33167==
==33167== Invalid write of size 4
==33167== at 0x805A542: parse_7zip_uint64 (archive_read_support_format_7zip.c:1675)
==33167== by 0x805B9C0: read_SubStreamsInfo (archive_read_support_format_7zip.c:2191)
==33167== by 0x805BF42: read_StreamsInfo (archive_read_support_format_7zip.c:2320)
==33167== by 0x805C0ED: read_Header (archive_read_support_format_7zip.c:2390)
==33167== by 0x805D5DE: slurp_central_directory (archive_read_support_format_7zip.c:2927)
==33167== by 0x805834F: archive_read_format_7zip_read_header (archive_read_support_format_7zip.c:640)
==33167== by 0x804E3C6: _archive_read_next_header2 (archive_read.c:645)
==33167== by 0x804E47A: _archive_read_next_header (archive_read.c:683)
==33167== by 0x8088A06: archive_read_next_header (archive_virtual.c:148)
==33167== by 0x804A178: extract (ext.c:55)
==33167== by 0x804A271: main (ext.c:83)
==33167== Address 0x445cd2c is 4 bytes after a block of size 8 alloc'd
==33167== at 0x402C109: calloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==33167== by 0x805B8FA: read_SubStreamsInfo (archive_read_support_format_7zip.c:2169)
==33167== by 0x805BF42: read_StreamsInfo (archive_read_support_format_7zip.c:2320)
==33167== by 0x805C0ED: read_Header (archive_read_support_format_7zip.c:2390)
==33167== by 0x805D5DE: slurp_central_directory (archive_read_support_format_7zip.c:2927)
==33167== by 0x805834F: archive_read_format_7zip_read_header (archive_read_support_format_7zip.c:640)
==33167== by 0x804E3C6: _archive_read_next_header2 (archive_read.c:645)
==33167== by 0x804E47A: _archive_read_next_header (archive_read.c:683)
==33167== by 0x8088A06: archive_read_next_header (archive_virtual.c:148)
==33167== by 0x804A178: extract (ext.c:55)
==33167== by 0x804A271: main (ext.c:83)
==33167==
==33167== Invalid read of size 4
==33167== at 0x805B9D8: read_SubStreamsInfo (archive_read_support_format_7zip.c:2193)
==33167== by 0x805BF42: read_StreamsInfo (archive_read_support_format_7zip.c:2320)
==33167== by 0x805C0ED: read_Header (archive_read_support_format_7zip.c:2390)
==33167== by 0x805D5DE: slurp_central_directory (archive_read_support_format_7zip.c:2927)
==33167== by 0x805834F: archive_read_format_7zip_read_header (archive_read_support_format_7zip.c:640)
==33167== by 0x804E3C6: _archive_read_next_header2 (archive_read.c:645)
==33167== by 0x804E47A: _archive_read_next_header (archive_read.c:683)
==33167== by 0x8088A06: archive_read_next_header (archive_virtual.c:148)
==33167== by 0x804A178: extract (ext.c:55)
==33167== by 0x804A271: main (ext.c:83)
==33167== Address 0x445cd2c is 4 bytes after a block of size 8 alloc'd
==33167== at 0x402C109: calloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==33167== by 0x805B8FA: read_SubStreamsInfo (archive_read_support_format_7zip.c:2169)
==33167== by 0x805BF42: read_StreamsInfo (archive_read_support_format_7zip.c:2320)
==33167== by 0x805C0ED: read_Header (archive_read_support_format_7zip.c:2390)
==33167== by 0x805D5DE: slurp_central_directory (archive_read_support_format_7zip.c:2927)
==33167== by 0x805834F: archive_read_format_7zip_read_header (archive_read_support_format_7zip.c:640)
==33167== by 0x804E3C6: _archive_read_next_header2 (archive_read.c:645)
==33167== by 0x804E47A: _archive_read_next_header (archive_read.c:683)
==33167== by 0x8088A06: archive_read_next_header (archive_virtual.c:148)
==33167== by 0x804A178: extract (ext.c:55)
==33167== by 0x804A271: main (ext.c:83)
==33167==
==33167== Invalid read of size 4
==33167== at 0x805B9DB: read_SubStreamsInfo (archive_read_support_format_7zip.c:2193)
==33167== by 0x805BF42: read_StreamsInfo (archive_read_support_format_7zip.c:2320)
==33167== by 0x805C0ED: read_Header (archive_read_support_format_7zip.c:2390)
==33167== by 0x805D5DE: slurp_central_directory (archive_read_support_format_7zip.c:2927)
==33167== by 0x805834F: archive_read_format_7zip_read_header (archive_read_support_format_7zip.c:640)
==33167== by 0x804E3C6: _archive_read_next_header2 (archive_read.c:645)
==33167== by 0x804E47A: _archive_read_next_header (archive_read.c:683)
==33167== by 0x8088A06: archive_read_next_header (archive_virtual.c:148)
==33167== by 0x804A178: extract (ext.c:55)
==33167== by 0x804A271: main (ext.c:83)
==33167== Address 0x445cd28 is 0 bytes after a block of size 8 alloc'd
==33167== at 0x402C109: calloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==33167== by 0x805B8FA: read_SubStreamsInfo (archive_read_support_format_7zip.c:2169)
==33167== by 0x805BF42: read_StreamsInfo (archive_read_support_format_7zip.c:2320)
==33167== by 0x805C0ED: read_Header (archive_read_support_format_7zip.c:2390)
==33167== by 0x805D5DE: slurp_central_directory (archive_read_support_format_7zip.c:2927)
==33167== by 0x805834F: archive_read_format_7zip_read_header (archive_read_support_format_7zip.c:640)
==33167== by 0x804E3C6: _archive_read_next_header2 (archive_read.c:645)
==33167== by 0x804E47A: _archive_read_next_header (archive_read.c:683)
==33167== by 0x8088A06: archive_read_next_header (archive_virtual.c:148)
==33167== by 0x804A178: extract (ext.c:55)
==33167== by 0x804A271: main (ext.c:83)
==33167==
Damaged 7-Zip archive
==33167==
==33167== HEAP SUMMARY:
==33167== in use at exit: 169,758 bytes in 199 blocks
==33167== total heap usage: 199 allocs, 0 frees, 169,758 bytes allocated
==33167==
==33167== LEAK SUMMARY:
==33167== definitely lost: 0 bytes in 0 blocks
==33167== indirectly lost: 0 bytes in 0 blocks
==33167== possibly lost: 0 bytes in 0 blocks
==33167== still reachable: 169,758 bytes in 199 blocks
==33167== suppressed: 0 bytes in 0 blocks
==33167== Rerun with --leak-check=full to see details of leaked memory
==33167==
==33167== For counts of detected and suppressed errors, rerun with: -v
==33167== ERROR SUMMARY: 564 errors from 8 contexts (suppressed: 0 from 0)
2016-04-19 - Vendor Disclosure
2016-06-18 - Public Release
Discovered by Marcin ‘Icewall’ Noga of Cisco Talos