Project: minizip-ng (zlib-ng/minizip-ng), language: c++
Vulnerability instance: minizip.cve-2023-48106
Sanitizer: address

================= Bug Report (1/1) ==================
## Source: GitHub Issue
## URL: https://github.com/zlib-ng/minizip-ng/issues/740
## Description:
Issue: zlib-ng/minizip-ng#740
Title: Heap-buffer-overflow in mz_os.c:188 in mz_path_resolve
State: closed
Created by: Akane0721
Created at: 2023-11-08 12:26:58+00:00
Labels: fixed, security, bug

Issue Body:
#### Description
heap-buffer-overflow (/minizip-ng/build/minizip+0x976d) in mz_path_resolve
#### Version
```bash
$ ./minizip -h
minizip-ng 4.0.2 - https://github.com/zlib-ng/minizip-ng
---------------------------------------------------
-h 
Usage: minizip [-x][-d dir|-l|-e][-o][-f][-y][-c cp][-a][-0 to -9][-b|-m|-t][-k 512][-p pwd][-s] file.zip [files]

  -x  Extract files
  -l  List files
  -d  Destination directory
  -e  Erase files
  -o  Overwrite existing files
  -c  File names use cp437 encoding (or specified codepage)
  -a  Append to existing zip file
  -i  Include full path of files
  -f  Follow symbolic links
  -y  Store symbolic links
  -v  Verbose info
  -0  Store only
  -1  Compress faster
  -9  Compress better
  -k  Disk size in KB
  -z  Zip central directory
  -p  Encryption password
  -s  AES encryption
  -b  BZIP2 compression
  -m  LZMA compression
  -n  XZ compression
  -t  ZSTD compression
```
#### Replay
```bash
git clone https://github.com/zlib-ng/minizip-ng.git
cd minizip-ng/
CC="gcc -fsanitize=address" CXX="g++ -fsanitize=address" cmake -S . -B build -D MZ_BUILD_TESTS=ON
cmake --build build
./minizip -x -o poc1
```
#### ASAN
```bash
-x -o /root/poc/minizip/poc1 
Archive /root/poc/minizip/poc1
=================================================================
==3028155==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60d0000001df at pc 0x55555555d76e bp 0x7fffffffddf0 sp 0x7fffffffdde0
READ of size 1 at 0x60d0000001df thread T0
    #0 0x55555555d76d in mz_path_resolve (/root/test/minizip-ng/build/minizip+0x976d)
    #1 0x555555573c77 in mz_zip_reader_save_all (/root/test/minizip-ng/build/minizip+0x1fc77)
    #2 0x55555555af70 in minizip_extract (/root/test/minizip-ng/build/minizip+0x6f70)
    #3 0x55555555c7ec in main (/root/test/minizip-ng/build/minizip+0x87ec)
    #4 0x7ffff73a9082 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x24082)
    #5 0x555555558aed in _start (/root/test/minizip-ng/build/minizip+0x4aed)

0x60d0000001df is located 1 bytes to the left of 129-byte region [0x60d0000001e0,0x60d000000261)
allocated by thread T0 here:
    #0 0x7ffff76a0808 in __interceptor_malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cc:144
    #1 0x55555557391b in mz_zip_reader_save_all (/root/test/minizip-ng/build/minizip+0x1f91b)
    #2 0x55555555af70 in minizip_extract (/root/test/minizip-ng/build/minizip+0x6f70)
    #3 0x55555555c7ec in main (/root/test/minizip-ng/build/minizip+0x87ec)
    #4 0x7ffff73a9082 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x24082)

SUMMARY: AddressSanitizer: heap-buffer-overflow (/root/test/minizip-ng/build/minizip+0x976d) in mz_path_resolve
Shadow bytes around the buggy address:
  0x0c1a7fff7fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c1a7fff7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c1a7fff8000: fa fa fa fa fa fa fa fa 00 00 00 00 00 00 00 00
  0x0c1a7fff8010: 00 00 00 00 00 00 00 00 01 fa fa fa fa fa fa fa
  0x0c1a7fff8020: fa fa 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0c1a7fff8030: 00 00 01 fa fa fa fa fa fa fa fa[fa]00 00 00 00
  0x0c1a7fff8040: 00 00 00 00 00 00 00 00 00 00 00 00 01 fa fa fa
  0x0c1a7fff8050: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c1a7fff8060: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c1a7fff8070: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c1a7fff8080: 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
  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
  Shadow gap:              cc
==3028155==ABORTING
```
#### POC
https://github.com/Akane0721/POC/blob/f37d805631e0a15bea1f15b6e1edfb3246a2e0fc/minizip-ng/poc1
#### Environment
```
Ubuntu 20.04
Clang 10.0.0
gcc 9.4.0
```



Comments:
Comment by pmqs on 2023-11-08 19:40:18+00:00:
Where did `poc1` come from? It is a badly corrupted zip file

minizip doesn't complain about it

```
$ ./minizip -l poc1
minizip-ng 4.0.1 - https://github.com/zlib-ng/minizip-ng
---------------------------------------------------
-l poc1 
      Packed     Unpacked Ratio Method   Attribs Date     Time  CRC-32     Name
      ------     -------- ----- ------   ------- ----     ----  ------     ----
   805503117   2018794583   39% deflate         0 11-07-14 04:30 f0c14f39   l//../////////////0W/T/.�TU//�
```

But this is what `unzip` thinks about it
```
$ unzip -l poc1
Archive:  poc1
  End-of-central-directory signature not found.  Either this file is not
  a zipfile, or it constitutes one disk of a multi-part archive.  In the
  latter case the central directory and zipfile comment will be found on
  the last disk(s) of this archive.
unzip:  cannot find zipfile directory in one of poc1 or
        poc1.zip, and cannot find poc1.ZIP, period.
```

same with `7z`

```
$ 7z l  ~/Downloads/poc1

7-Zip [64] 16.02 : Copyright (c) 1999-2016 Igor Pavlov : 2016-05-21
p7zip Version 16.02 (locale=en_GB.UTF-8,Utf16=on,HugeFiles=on,64 bits,2 CPUs Intel(R) Core(TM) i7-6700 CPU @ 3.40GHz (506E3),ASM,AES-NI)

Scanning the drive for archives:
1 file, 253 bytes (1 KiB)      

Listing archive: /home/paul/Downloads/poc1


ERROR: /home/paul/Downloads/poc1 : Can not open the file as archive



Errors: 1
```

the file appears to have a single local header that contains some crazy values.

```
0000 0003 0004 50 4B 03 04 LOCAL HEADER #1       04034B50 (67324752)
0004 0004 0001 14          Extract Zip Spec      14 (20) '2.0'
0005 0005 0001 00          Extract OS            00 (0) 'MS-DOS'
0006 0007 0002 02 ED       General Purpose Flag  ED02 (60674)
                           [Bits 1-2]            1 'Maximum Compression'
                           [Bit 11]              1 'Language Encoding'
                           [Bit 13]              1 'Encrypted Central Dir'
0008 0009 0002 08 00       Compression Method    0008 (8) 'Deflated'
000A 000D 0004 DC 23 67 45 Last Mod Time         456723DC (1164387292) 'Fri Nov  7 04:30:56 2014'
000E 0011 0004 39 4F C1 F0 CRC                   F0C14F39 (4039200569)
0012 0015 0004 8D 00 03 30 Compressed Length     3003008D (805503117)
0016 0019 0004 57 5C 54 78 Uncompressed Length   78545C57 (2018794583)
001A 001B 0002 78 00       Filename Length       0078 (120)
001C 001D 0002 00 00       Extra Length          0000 (0)
#
# WARNING: Offset 0x1E: Could not decode 'utf8' Filename: utf8 "\x81" does not map to Unicode
#
001E 0095 0078 6C 2F 2F 2E Filename              'l//../////////////0W\T\.TU// Ô  ¿   nwGF¯ÀÑIí  Ü#gE9OÁð  CW\meri!kUTTUx   limer C   Èÿ mÊG
               2E 2F 2F 2F                       U\nè ¶Í`eÒfÿ  ,8'
               2F 2F 2F 2F
               2F 2F 2F 2F
               2F 2F 30 57
               5C 54 5C 2E
               81 54 55 2F
               2F 01 D4 01
               00 BF 00 00
               00 6E 77 47
               46 AF C0 95
               D1 49 ED 08
               00 DC 23 67
               45 39 4F C1
               F0 8D 00 03
               43 57 5C 6D
               65 72 69 21
               6B 55 54 54
               55 78 00 00
               00 6C 69 6D
               65 72 03 43
               00 00 00 C8
               FF 7F 8D 9E
               E4 6D 8C CA
               47 8D 00 00
               00 8A 55 5C
               98 BA 6E E8
               03 B6 CD 60
               65 D2 66 FF
               7F 09 2C 38
#
# FATAL: Offset 0x96: file truncated while reading 'PAYLOAD'
#        Expected 0x3003008D bytes, but only 0x67 available . 
#
```

---

Comment by Akane0721 on 2023-11-09 07:02:08+00:00:
> Where did `poc1` come from? It is a badly corrupted zip file
> 
> minizip doesn't complain about it
> 
> ```
> $ ./minizip -l poc1
> minizip-ng 4.0.1 - https://github.com/zlib-ng/minizip-ng
> ---------------------------------------------------
> -l poc1 
>       Packed     Unpacked Ratio Method   Attribs Date     Time  CRC-32     Name
>       ------     -------- ----- ------   ------- ----     ----  ------     ----
>    805503117   2018794583   39% deflate         0 11-07-14 04:30 f0c14f39   l//../////////////0W/T/.�TU//�
> ```
> 
> But this is what `unzip` thinks about it
> 
> ```
> $ unzip -l poc1
> Archive:  poc1
>   End-of-central-directory signature not found.  Either this file is not
>   a zipfile, or it constitutes one disk of a multi-part archive.  In the
>   latter case the central directory and zipfile comment will be found on
>   the last disk(s) of this archive.
> unzip:  cannot find zipfile directory in one of poc1 or
>         poc1.zip, and cannot find poc1.ZIP, period.
> ```
> 
> same with `7z`
> 
> ```
> $ 7z l  ~/Downloads/poc1
> 
> 7-Zip [64] 16.02 : Copyright (c) 1999-2016 Igor Pavlov : 2016-05-21
> p7zip Version 16.02 (locale=en_GB.UTF-8,Utf16=on,HugeFiles=on,64 bits,2 CPUs Intel(R) Core(TM) i7-6700 CPU @ 3.40GHz (506E3),ASM,AES-NI)
> 
> Scanning the drive for archives:
> 1 file, 253 bytes (1 KiB)      
> 
> Listing archive: /home/paul/Downloads/poc1
> 
> 
> ERROR: /home/paul/Downloads/poc1 : Can not open the file as archive
> 
> 
> 
> Errors: 1
> ```
> 
> the file appears to have a single local header that contains some crazy values.
> 
> ```
> 0000 0003 0004 50 4B 03 04 LOCAL HEADER #1       04034B50 (67324752)
> 0004 0004 0001 14          Extract Zip Spec      14 (20) '2.0'
> 0005 0005 0001 00          Extract OS            00 (0) 'MS-DOS'
> 0006 0007 0002 02 ED       General Purpose Flag  ED02 (60674)
>                            [Bits 1-2]            1 'Maximum Compression'
>                            [Bit 11]              1 'Language Encoding'
>                            [Bit 13]              1 'Encrypted Central Dir'
> 0008 0009 0002 08 00       Compression Method    0008 (8) 'Deflated'
> 000A 000D 0004 DC 23 67 45 Last Mod Time         456723DC (1164387292) 'Fri Nov  7 04:30:56 2014'
> 000E 0011 0004 39 4F C1 F0 CRC                   F0C14F39 (4039200569)
> 0012 0015 0004 8D 00 03 30 Compressed Length     3003008D (805503117)
> 0016 0019 0004 57 5C 54 78 Uncompressed Length   78545C57 (2018794583)
> 001A 001B 0002 78 00       Filename Length       0078 (120)
> 001C 001D 0002 00 00       Extra Length          0000 (0)
> #
> # WARNING: Offset 0x1E: Could not decode 'utf8' Filename: utf8 "\x81" does not map to Unicode
> #
> 001E 0095 0078 6C 2F 2F 2E Filename              'l//../////////////0W\T\.TU// Ô  ¿   nwGF¯ÀÑIí  Ü#gE9OÁð  CW\meri!kUTTUx   limer C   Èÿ mÊG
>                2E 2F 2F 2F                       U\nè ¶Í`eÒfÿ  ,8'
>                2F 2F 2F 2F
>                2F 2F 2F 2F
>                2F 2F 30 57
>                5C 54 5C 2E
>                81 54 55 2F
>                2F 01 D4 01
>                00 BF 00 00
>                00 6E 77 47
>                46 AF C0 95
>                D1 49 ED 08
>                00 DC 23 67
>                45 39 4F C1
>                F0 8D 00 03
>                43 57 5C 6D
>                65 72 69 21
>                6B 55 54 54
>                55 78 00 00
>                00 6C 69 6D
>                65 72 03 43
>                00 00 00 C8
>                FF 7F 8D 9E
>                E4 6D 8C CA
>                47 8D 00 00
>                00 8A 55 5C
>                98 BA 6E E8
>                03 B6 CD 60
>                65 D2 66 FF
>                7F 09 2C 38
> #
> # FATAL: Offset 0x96: file truncated while reading 'PAYLOAD'
> #        Expected 0x3003008D bytes, but only 0x67 available . 
> #
> ```


poc1 is a malformed zip file generated by fuzzer. I used the "-x" flag when testing and it came into a heap-buffer-overflow crash. So maybe you could give a proper prompt when using "-x" to extract malformed files like poc1?

---

Comment by pmqs on 2023-11-09 09:09:15+00:00:
> poc1 is a malformed zip file generated by fuzzer. I used the "-x" flag when testing and it came into a heap-buffer-overflow crash. So maybe you could give a proper prompt when using "-x" to extract malformed files like poc1?

ok.

Running without ASAN triggers a core. Never a good thing to do

```
$ ./minizip -x  poc1
minizip-ng 4.0.2 - https://github.com/zlib-ng/minizip-ng
---------------------------------------------------
-x poc1 
Archive poc1
Extracting l//../////////////0W/T/.�TU//�
double free or corruption (out)
Aborted (core dumped)
```
When I build with ASAN I see the line numbers where the issue is triggered

```
$ ./minizip -x -o poc1
minizip-ng 4.0.2 - https://github.com/zlib-ng/minizip-ng
---------------------------------------------------
-x -o poc1 
Archive poc1
=================================================================
==49454==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60d0000001df at pc 0x55f2bdfcdfec bp 0x7ffd97501c40 sp 0x7ffd97501c30
READ of size 1 at 0x60d0000001df thread T0
    #0 0x55f2bdfcdfeb in mz_path_resolve /home/paul/git/minizip-ng/mz_os.c:188
    #1 0x55f2bdfe577b in mz_zip_reader_save_all /home/paul/git/minizip-ng/mz_zip_rw.c:883
    #2 0x55f2bdfcb6f0 in minizip_extract /home/paul/git/minizip-ng/minizip.c:379
    #3 0x55f2bdfcd02c in main /home/paul/git/minizip-ng/minizip.c:654
    #4 0x7f874d2280cf in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
    #5 0x7f874d228188 in __libc_start_main_impl ../csu/libc-start.c:360
    #6 0x55f2bdfc9264 in _start (/home/paul/git/minizip-ng/build/minizip+0x7264) (BuildId: 576262a7c293cbb21845b25bd97a13cb33d9dd27)

0x60d0000001df is located 1 bytes before 129-byte region [0x60d0000001e0,0x60d000000261)
allocated by thread T0 here:
    #0 0x7f874dcddde0 in __interceptor_realloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:85
    #1 0x55f2bdfe548d in mz_zip_reader_save_all /home/paul/git/minizip-ng/mz_zip_rw.c:861
    #2 0x55f2bdfcb6f0 in minizip_extract /home/paul/git/minizip-ng/minizip.c:379
    #3 0x55f2bdfcd02c in main /home/paul/git/minizip-ng/minizip.c:654
    #4 0x7f874d2280cf in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58

SUMMARY: AddressSanitizer: heap-buffer-overflow /home/paul/git/minizip-ng/mz_os.c:188 in mz_path_resolve
Shadow bytes around the buggy address:
  0x60cfffffff00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x60cfffffff80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x60d000000000: fa fa fa fa fa fa fa fa 00 00 00 00 00 00 00 00
  0x60d000000080: 00 00 00 00 00 00 00 00 01 fa fa fa fa fa fa fa
  0x60d000000100: fa fa 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x60d000000180: 00 00 01 fa fa fa fa fa fa fa fa[fa]00 00 00 00
  0x60d000000200: 00 00 00 00 00 00 00 00 00 00 00 00 01 fa fa fa
  0x60d000000280: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x60d000000300: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x60d000000380: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x60d000000400: 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
  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
==49454==ABORTING
```

---

Comment by pmqs on 2023-11-12 20:43:20+00:00:
The issue with this one is a filename of the form `x/../fred`. The code in `mz_path_resolve` tries to remove the `..` by walking backwards to the preceding `/`. It wants to end up with the filename `fred`, but in this case there isn't a preceeding `/`, so it walks past the start of the buffer.

See [test1.zip](https://github.com/zlib-ng/minizip-ng/files/13329082/test1.zip) for  small zip that reproduces the issue.

---

Comment by nmoinvaz on 2023-11-13 18:05:45+00:00:
Fixed in 4.0.3. Thank you!

Attachments:
https://github.com/zlib-ng/minizip-ng/files/13329082/test1.zip

Commit References:
f37d805631e0a15bea1f15b6e1edfb3246a2e0fc
576262a7c293cbb21845b25bd97a13cb33d9dd27

Build script:
#!/bin/bash -eu
# Minimized build script with only core build commands
set -eu
cmake . -DCMAKE_C_FLAGS="$CFLAGS" -DCMAKE_CXX_FLAGS="$CXXFLAGS" -DMZ_BUILD_FUZZ_TESTS=ON
make clean -j$(nproc)
make -j$(nproc)

Dockerfile:
FROM hwiwonlee/secb.base:latest
RUN apt-get update && apt-get install -y make cmake pkg-config libssl-dev:i386
RUN git clone https://github.com/zlib-ng/minizip-ng minizip-ng
RUN git -C minizip-ng checkout 4f670cc7d7ad3a7a27ac3ed5e6c5ddf20956654b
WORKDIR $SRC/minizip-ng
COPY build.sh $SRC/

Additional files: