madler / zlib

A massively spiffy yet delicately unobtrusive compression library.
http://zlib.net/
Other
5.71k stars 2.45k forks source link

stack buffer overflow in miniunz #869

Closed asarubbo closed 9 months ago

asarubbo commented 1 year ago

Hello,

by fuzzing miniunz in zlib-1.3 I found two stack buffer overflow (maybe the same root cause). They are reproducible with the same testcase by just switching from list (-l) to extract (-x).

The first:

 ~ $ miniunz -l 2.crashes.zip 
MiniUnz 1.01b, demo of zLib + Unz package written by Gilles Vollant
more info at http://www.winimage.com/zLibDll/unzip.html

2.crashes.zip opened
  Length  Method     Size Ratio   Date    Time   CRC-32     Name
  ------  ------     ---- -----   ----    ----   ------     ----
       6  Stored        6 100%  15-18-16  12:53  7f84a974   foo.txt
       6  Stored 16777222 279620366%  08-04-16  12:53  7f84a974   fxt

       6  Stored        6 100%  08-04-16  12:53  7f84a974   fo
=================================================================
==43573==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7fcf83100220 at pc 0x000000442242 bp 0x7fffd72c4fb0 sp 0x7fffd72c4738
READ of size 257 at 0x7fcf83100220 thread T0
    #0 0x442241 in printf_common(void*, char const*, __va_list_tag*) /var/tmp/portage/sys-libs/compiler-rt-sanitizers-16.0.6/work/compiler-rt/lib/asan/../sanitizer_common/sanitizer_common_interceptors_format.inc:553:9
    #1 0x443b4a in __interceptor_vprintf /var/tmp/portage/sys-libs/compiler-rt-sanitizers-16.0.6/work/compiler-rt/lib/asan/../sanitizer_common/sanitizer_common_interceptors.inc:1714:1
    #2 0x443b4a in printf /var/tmp/portage/sys-libs/compiler-rt-sanitizers-16.0.6/work/compiler-rt/lib/asan/../sanitizer_common/sanitizer_common_interceptors.inc:1772:1
    #3 0x4f2f38 in do_list /var/tmp/portage/sys-libs/zlib-1.3-r1/work/zlib-1.3/contrib/minizip/miniunz.c:285:9
    #4 0x4f2f38 in main /var/tmp/portage/sys-libs/zlib-1.3-r1/work/zlib-1.3/contrib/minizip/miniunz.c:611:21
    #5 0x7fcf84a1a676 in __libc_start_call_main /var/tmp/portage/sys-libs/glibc-2.37-r3/work/glibc-2.37/csu/../sysdeps/nptl/libc_start_call_main.h:58:16
    #6 0x7fcf84a1a734 in __libc_start_main /var/tmp/portage/sys-libs/glibc-2.37-r3/work/glibc-2.37/csu/../csu/libc-start.c:360:3
    #7 0x41d610 in _start (/usr/bin/miniunz+0x41d610)

Address 0x7fcf83100220 is located in stack of thread T0 at offset 544 in frame
    #0 0x4f1d7f in main /var/tmp/portage/sys-libs/zlib-1.3-r1/work/zlib-1.3/contrib/minizip/miniunz.c:510

  This frame has 11 object(s):
    [32, 36) 'opt_extract_without_path.addr.i214' (line 507)
    [48, 52) 'opt_overwrite.addr.i215' (line 507)
    [64, 68) 'opt_extract_without_path.addr.i' (line 491)
    [80, 84) 'opt_overwrite.addr.i' (line 491)
    [96, 112) 'gi.i205' (line 466)
    [128, 149) 'number.i101.i' (line 206)
    [192, 213) 'number.i.i' (line 206)
    [256, 272) 'gi.i' (line 233)
    [288, 544) 'filename_inzip.i' (line 243)
    [608, 744) 'file_info.i' (line 244) <== Memory access at offset 544 partially underflows this variable
    [816, 1088) 'filename_try' (line 514)
HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
      (longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-buffer-overflow /var/tmp/portage/sys-libs/compiler-rt-sanitizers-16.0.6/work/compiler-rt/lib/asan/../sanitizer_common/sanitizer_common_interceptors_format.inc:553:9 in printf_common(void*, char const*, __va_list_tag*)
Shadow bytes around the buggy address:
  0x7fcf830fff80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x7fcf83100000: f1 f1 f1 f1 f8 f2 f8 f2 f8 f2 f8 f2 f8 f8 f2 f2
  0x7fcf83100080: f8 f8 f8 f2 f2 f2 f2 f2 f8 f8 f8 f2 f2 f2 f2 f2
  0x7fcf83100100: 00 00 f2 f2 00 00 00 00 00 00 00 00 00 00 00 00
  0x7fcf83100180: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x7fcf83100200: 00 00 00 00[f2]f2 f2 f2 f2 f2 f2 f2 00 00 00 00
  0x7fcf83100280: 00 00 00 00 00 00 00 00 00 00 00 00 00 f2 f2 f2
  0x7fcf83100300: f2 f2 f2 f2 f2 f2 00 00 00 00 00 00 00 00 00 00
  0x7fcf83100380: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x7fcf83100400: 00 00 00 00 00 00 00 00 f3 f3 f3 f3 f3 f3 f3 f3
  0x7fcf83100480: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
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
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  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
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==43573==ABORTING
Aborted

The second:

 ~ $ miniunz -x -o 2.crashes.zip 
MiniUnz 1.01b, demo of zLib + Unz package written by Gilles Vollant
more info at http://www.winimage.com/zLibDll/unzip.html

2.crashes.zip opened
 extracting: foo.txt
 extracting: fxt

 extracting: fo
=================================================================
==60555==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7fb0c9d011a0 at pc 0x0000004f3987 bp 0x7fff98056090 sp 0x7fff98056088
READ of size 1 at 0x7fb0c9d011a0 thread T0
    #0 0x4f3986 in do_extract_currentfile /var/tmp/portage/sys-libs/zlib-1.3-r1/work/zlib-1.3/contrib/minizip/miniunz.c:334:13
    #1 0x4f3353 in do_extract /var/tmp/portage/sys-libs/zlib-1.3-r1/work/zlib-1.3/contrib/minizip/miniunz.c:475:13
    #2 0x4f3353 in main /var/tmp/portage/sys-libs/zlib-1.3-r1/work/zlib-1.3/contrib/minizip/miniunz.c:625:25
    #3 0x7fb0cb79f676 in __libc_start_call_main /var/tmp/portage/sys-libs/glibc-2.37-r3/work/glibc-2.37/csu/../sysdeps/nptl/libc_start_call_main.h:58:16
    #4 0x7fb0cb79f734 in __libc_start_main /var/tmp/portage/sys-libs/glibc-2.37-r3/work/glibc-2.37/csu/../csu/libc-start.c:360:3
    #5 0x41d610 in _start (/usr/bin/miniunz+0x41d610)

Address 0x7fb0c9d011a0 is located in stack of thread T0 at offset 416 in frame
    #0 0x4f36af in do_extract_currentfile /var/tmp/portage/sys-libs/zlib-1.3-r1/work/zlib-1.3/contrib/minizip/miniunz.c:307

  This frame has 5 object(s):
    [32, 48) 'ut.i' (line 99)
    [64, 120) 'newdate.i' (line 100)
    [160, 416) 'filename_inzip' (line 308) <== Memory access at offset 416 overflows this variable
    [480, 616) 'file_info' (line 316)
    [688, 816) 'answer' (line 375)
HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
      (longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-buffer-overflow /var/tmp/portage/sys-libs/zlib-1.3-r1/work/zlib-1.3/contrib/minizip/miniunz.c:334:13 in do_extract_currentfile
Shadow bytes around the buggy address:
  0x7fb0c9d00f00: f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5
  0x7fb0c9d00f80: f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5
  0x7fb0c9d01000: f1 f1 f1 f1 f8 f8 f2 f2 f8 f8 f8 f8 f8 f8 f8 f2
  0x7fb0c9d01080: f2 f2 f2 f2 00 00 00 00 00 00 00 00 00 00 00 00
  0x7fb0c9d01100: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x7fb0c9d01180: 00 00 00 00[f2]f2 f2 f2 f2 f2 f2 f2 00 00 00 00
  0x7fb0c9d01200: 00 00 00 00 00 00 00 00 00 00 00 00 00 f2 f2 f2
  0x7fb0c9d01280: f2 f2 f2 f2 f2 f2 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8
  0x7fb0c9d01300: f8 f8 f8 f8 f8 f8 f3 f3 f3 f3 f3 f3 00 00 00 00
  0x7fb0c9d01380: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x7fb0c9d01400: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
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
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  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
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==60555==ABORTING
Aborted

If I can do further, please let me know. Testcase: 2.crashes.zip

zmodem commented 12 months ago

I found two stack buffer overflow (maybe the same root cause).

It's two instances of the same problem. One results in "printf of non-null-terminated string", and the other is an out-of-bounds read while copying the filename.

For -l, it's crashing in this printf:

https://github.com/madler/zlib/blob/09155eaa2f9270dc4ed1fa13e2b4b2613e6e4851/contrib/minizip/miniunz.c#L285-L291

filename_inzip is declared here:

https://github.com/madler/zlib/blob/09155eaa2f9270dc4ed1fa13e2b4b2613e6e4851/contrib/minizip/miniunz.c#L243

and gets filled by

https://github.com/madler/zlib/blob/09155eaa2f9270dc4ed1fa13e2b4b2613e6e4851/contrib/minizip/miniunz.c#L248

The problem is that unzGetCurrentFileInfo64 will only null terminate the filename if there is room left in the buffer:

https://github.com/madler/zlib/blob/09155eaa2f9270dc4ed1fa13e2b4b2613e6e4851/contrib/minizip/unzip.c#L858-L860

If there's not enough room, the buffer is filled to its capacity, without any null terminator.

The -x crash happens for similar reasons - filename_inzip not being null terminated - here:

https://github.com/madler/zlib/blob/09155eaa2f9270dc4ed1fa13e2b4b2613e6e4851/contrib/minizip/miniunz.c#L333-L339

The easiest way to fix this is probably to have the calling code always null-terminate the filename_inzip buffer, by doing something like:

char filename_inzip[257] = {0};
...
err = unzGetCurrentFileInfo64(uf,&file_info,filename_inzip,sizeof(filename_inzip) - 1,NULL,0,NULL,0);

That would fix the crashes in miniunz, but it's pretty likely that there's other code using minizip which has the same problem. (See e.g. this old Chromium fix.) It would perhaps be worth considering a deeper fix, making the API less dangerous by ensuring that unzGetCurrentFileInfo64 always null terminates the buffer, though that could break any code which relies on the old behavior.

Neustradamus commented 9 months ago

@zmodem: A fix has been done for it?

cc: @madler

Neustradamus commented 9 months ago

Can you look with the latest release build, 1.3.1?

zmodem commented 9 months ago

Can you look with the latest release build, 1.3.1?

The problem is still there:

$ clang -fsanitize=address -g contrib/minizip/miniunz.c contrib/minizip/unzip.c contrib/minizip/ioapi.c inflate.c crc32.c adler32.c inftrees.c inffast.c zutil.c && ./a.out -l 2.crashes.zip
...
==3861855==ERROR: AddressSanitizer: stack-buffer-overflow on address

Making the API safer would be good, but could also be more disruptive, so I sent https://github.com/madler/zlib/pull/911 to just fix the calling code in miniunz.c.

madler commented 9 months ago

Fixed by increasing buffer sizes.

asarubbo commented 9 months ago

Fixed by increasing buffer sizes.

By looking at the git history I don't see any commits that mentioned this. Are you referring to 15c45adb76e81a7e3a8a9e17b2a56eb90f668f44 or what is the commit the fixes this issue?

Neustradamus commented 9 months ago

@asarubbo: @madler has not pushed computer changes, I have already specified in another ticket but he has not pushed yet :/

@madler: Can you push all your changes after 1.3.1 release build?

Thanks in advance.