Headline
CVE-2018-11496: heap-use-after-free in read_stream (stream.c:1756) · Issue #96 · ckolivas/lrzip
In Long Range Zip (aka lrzip) 0.631, there is a use-after-free in read_stream in stream.c, because decompress_file in lrzip.c lacks certain size validation.
In the latest commit master-ed51e1, there is a heap-use-after-free in read_stream function (stream.c:1756).
With ASAN cflags "-fsanitize=address", use the POC attached will trigger the vulnerability.
Command to reproduce: ./lrzip -t lrzip-uaf-read_stream.lrz
The ASAN backtrace info:
READ of size 1 at 0x60200000eef0 thread T0
#0 0x7f5752d26934 in __asan_memcpy (/usr/lib/gcc/x86_64-linux-gnu/5/libasan.so+0x8c934)
#1 0x414655 in memcpy /usr/include/x86_64-linux-gnu/bits/string3.h:53
#2 0x414655 in read_stream /opt/csn/lrzip/stream.c:1756
#3 0x40ee8c in read_vchars /opt/csn/lrzip/runzip.c:79
#4 0x40ee8c in read_header /opt/csn/lrzip/runzip.c:147
#5 0x40ee8c in runzip_chunk /opt/csn/lrzip/runzip.c:316
#6 0x40ee8c in runzip_fd /opt/csn/lrzip/runzip.c:384
#7 0x407db9 in decompress_file /opt/csn/lrzip/lrzip.c:838
#8 0x403a74 in main /opt/csn/lrzip/main.c:675
#9 0x7f57515e082f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
#10 0x404888 in _start (/opt/csn/lrzip/lrzip+0x404888)
0x60200000eef0 is located 0 bytes inside of 10-byte region [0x60200000eef0,0x60200000eefa)
freed by thread T0 here:
#0 0x7f5752d322ca in __interceptor_free (/usr/lib/gcc/x86_64-linux-gnu/5/libasan.so+0x982ca)
#1 0x4146d0 in fill_buffer /opt/csn/lrzip/stream.c:1573
#2 0x4146d0 in read_stream /opt/csn/lrzip/stream.c:1764
previously allocated by thread T0 here:
#0 0x7f5752d32602 in malloc (/usr/lib/gcc/x86_64-linux-gnu/5/libasan.so+0x98602)
#1 0x414937 in fill_buffer /opt/csn/lrzip/stream.c:1651
#2 0x414937 in read_stream /opt/csn/lrzip/stream.c:1764
SUMMARY: AddressSanitizer: heap-use-after-free ??:0 __asan_memcpy
Shadow bytes around the buggy address:
0x0c047fff9d80: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c047fff9d90: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c047fff9da0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c047fff9db0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c047fff9dc0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
=>0x0c047fff9dd0: fa fa fa fa fa fa fa fa fa fa fd fd fa fa[fd]fd
0x0c047fff9de0: fa fa 00 00 fa fa 00 00 fa fa 00 00 fa fa 00 00
0x0c047fff9df0: fa fa 00 00 fa fa 00 00 fa fa 00 00 fa fa 04 fa
0x0c047fff9e00: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c047fff9e10: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c047fff9e20: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Heap right redzone: fb
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack partial redzone: f4
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
==85575==ABORTING
when compile without ASAN flags, lrzip will crash because of double free
Decompressing...
*** Error in `./lrzip': double free or corruption (fasttop): 0x0000000000ba9b60 ***
======= Backtrace: =========
/lib/x86_64-linux-gnu/libc.so.6(+0x777e5)[0x7f6191e817e5]
/lib/x86_64-linux-gnu/libc.so.6(+0x8037a)[0x7f6191e8a37a]
/lib/x86_64-linux-gnu/libc.so.6(cfree+0x4c)[0x7f6191e8e53c]
./lrzip[0x4146d1]
./lrzip[0x40ef03]
./lrzip[0x407dba]
./lrzip[0x403a75]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0)[0x7f6191e2a830]
./lrzip[0x404889]
======= Memory map: ========
00400000-00443000 r-xp 00000000 ca:02 655162 /opt/csn/lrzip/lrzip
00642000-00643000 r--p 00042000 ca:02 655162 /opt/csn/lrzip/lrzip
00643000-00644000 rw-p 00043000 ca:02 655162 /opt/csn/lrzip/lrzip
s->buf was freed in fill_buffer in stream.c:1573
1564 /* fill a buffer from a stream - return -1 on failure */ 1565 static int fill_buffer(rzip_control *control, struct stream_info *sinfo, struct stream *s, int streamno) 1566 { 1567 i64 u_len, c_len, last_head, padded_len, header_length, max_len; 1568 uchar enc_head[25 + SALT_LEN], blocksalt[SALT_LEN]; 1569 stream_thread_struct *st; 1570 bool new_thread = false; 1571 uchar c_type, *s_buf; 1572 1573 dealloc(s->buf);
in the below code, the s->buf may be freed in fill_buffer, but with while the s->buf will be used again in memcpy in line 1756, and may be free again in fill_buffer in 1764. which caused heap use after free and double free.
1750 while (len) { 1751 i64 n; 1752 1753 n = MIN(s->buflen - s->bufp, len); 1754 1755 if (n > 0) { 1756 memcpy(p, s->buf + s->bufp, n); 1757 s->bufp += n; 1758 p += n; 1759 len -= n; 1760 ret += n; 1761 } 1762 1763 if (len && s->bufp == s->buflen) { 1764 if (unlikely(fill_buffer(control, sinfo, s, streamno))) 1765 return -1; 1766 if (s->bufp == s->buflen) 1767 break; 1768 } 1769 }
lrzip-uaf-read_stream.lrz.zip