Menu

#277 A crafted png file leads to a heap-based buffer overflow bug in advpng(v2.1)

other
closed-fixed
nobody
None
5
2019-07-29
2019-02-27
M4x
No

Hi,
I found that a crafted png file can lead to an interger overflow bug, which lead to a heap-based buffer overflow bug futher. The vulnerable function is png_compress() of AdvanceCOMP:advpng:pngex.cc:55, version 2.1.

I attach my poc below. You can test it via
/path/to/advpng -z /path/to/poc

The backtrace and ASAN information are as follows:

backtrace

pwndbg> bt full
#0  __memcpy_avx_unaligned () at ../sysdeps/x86_64/multiarch/memcpy-avx-unaligned.S:245
No locals.
#1  0x0000000000405a31 in png_compress (level=..., out_ptr=..., out_size=@0x7fffffffd93c: 32767, img_ptr=0x662361 '\001' <repeats 32 times>, img_scanline=1, img_pixel=1, x=0, y=0, dx=4294967295, dy=1056) at pngex.cc:55
        p1 = 0x662361 '\001' <repeats 32 times>
        fil_ptr = {
          data = 0x660de0 "",
          own = true
        }
        fil_size = 0
        fil_scanline = 0
        z_ptr = {
          data = 0x660e00 "\230k1\367\377\177",
          own = true
        }
        z_size = 22
        i = 0
        p0 = 0x660de1 "k1\367\377\177"
        __PRETTY_FUNCTION__ = "void png_compress(shrink_t, data_ptr&, unsigned int&, const unsigned char*, unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, unsigned int)"
#2  0x000000000040706f in png_write (f=0x660d10, pix_width=4294967295, pix_height=1056, pix_pixel=1, pix_ptr=0x662361 '\001' <repeats 32 times>, pix_scanline=1, pal_ptr=0x660e30 '0' <repeats 15 times>, pal_size=15, rns_ptr=0x0, rns_size=0, level=...) at pngex.cc:355
        ihdr = "\377\377\377\377\000\000\004 \b\003\000\000"
        z_ptr = {
          data = 0x0,
          own = false
        }
        z_size = 32767
        __PRETTY_FUNCTION__ = "void png_write(adv_fz*, unsigned int, unsigned int, unsigned int, unsigned char*, unsigned int, unsigned char*, unsigned int, unsigned char*, unsigned int, shrink_t)"
#3  0x0000000000403420 in write_image (f=0x660d10, pix_width=4294967295, pix_height=1056, pix_pixel=1, pix_ptr=0x662361 '\001' <repeats 32 times>, pix_scanline=1, pal_ptr=0x660e30 '0' <repeats 15 times>, pal_size=15, rns_ptr=0x0, rns_size=0) at repng.cc:101
No locals.
#4  0x000000000040366e in convert_f (f_in=0x660c40, f_out=0x660d10) at repng.cc:160
        dat_ptr = 0x662360 ""
        dat_size = 1056
        pix_pixel = 1
        pix_width = 4294967295
        pix_height = 1056
        pal_ptr = 0x660e30 '0' <repeats 15 times>
        pal_size = 15
        rns_ptr = 0x0
        rns_size = 0
        pix_ptr = 0x662361 '\001' <repeats 32 times>
        pix_scanline = 1
#5  0x00000000004038be in convert_inplace (path="./heap-overflow.png") at repng.cc:193
        f_in = 0x660c40
        f_out = 0x660d10
        path_dst = "./heap-overflow.png.tmp1551203033"
        __PRETTY_FUNCTION__ = "void convert_inplace(const string&)"
        dst_size = 0
#6  0x00000000004040a4 in rezip_single (file="./heap-overflow.png", total_0=@0x7fffffffe300: 0, total_1=@0x7fffffffe308: 0) at repng.cc:283
        size_0 = 218
        size_1 = 0
        desc = ""
        __PRETTY_FUNCTION__ = "void rezip_single(const string&, long long unsigned int&, long long unsigned int&)"
#7  0x000000000040443f in rezip_all (argc=1, argv=0x7fffffffe578) at repng.cc:317
        i = 0
        total_0 = 0
        total_1 = 0
#8  0x0000000000404c8a in process (argc=3, argv=0x7fffffffe568) at repng.cc:476
        cmd = cmd_recompress
        c = -1
        __PRETTY_FUNCTION__ = "void process(int, char**)"
#9  0x0000000000404e54 in main (argc=3, argv=0x7fffffffe568) at repng.cc:489
No locals.
#10 0x00007ffff6f72830 in __libc_start_main (main=0x404e24 <main(int, char**)>, argc=3, argv=0x7fffffffe568, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffe558) at ../csu/libc-start.c:291
        result = <optimized out>
        unwind_buf = {
          cancel_jmp_buf = {{
              jmp_buf = {0, 2691200841703839888, 4206080, 140737488348512, 0, 0, -2691200322350786416, -2691184995872188272},
              mask_was_saved = 0
            }},
          priv = {
            pad = {0x0, 0x0, 0x7fffffffe588, 0x7ffff7ffe168},
            data = {
              prev = 0x0,
              cleanup = 0x0,
              canceltype = -6776
            }
          }
        }
        not_first_call = <optimized out>
#11 0x0000000000402e29 in _start ()
No symbol table info available.
pwndbg>

ASAN

ubuntu@VM-61-71-ubuntu:~$ ./advpng-asan -z ./heap-overflow.png
=================================================================
==11954==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x6190000068a0 at pc 0x7f5e2e4f0935 bp 0x7ffe805dec50 sp 0x7ffe805de3f8
READ of size 4294967295 at 0x6190000068a0 thread T0
    #0 0x7f5e2e4f0934 in __asan_memcpy (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x8c934)
    #1 0x411173 in memcpy /usr/include/x86_64-linux-gnu/bits/string3.h:53
    #2 0x411173 in png_compress(shrink_t, data_ptr&, unsigned int&, unsigned char const*, unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, unsigned int) /home/ubuntu/advancecomp-2.1/pngex.cc:55
    #3 0x41756f in png_write(adv_fz_struct*, unsigned int, unsigned int, unsigned int, unsigned char*, unsigned int, unsigned char*, unsigned int, unsigned char*, unsigned int, shrink_t) /home/ubuntu/advancecomp-2.1/pngex.cc:355
    #4 0x406d46 in write_image(adv_fz_struct*, unsigned int, unsigned int, unsigned int, unsigned char*, unsigned int, unsigned char*, unsigned int, unsigned char*, unsigned int) /home/ubuntu/advancecomp-2.1/repng.cc:101
    #5 0x406f2a in convert_f(adv_fz_struct*, adv_fz_struct*) /home/ubuntu/advancecomp-2.1/repng.cc:160
    #6 0x4071e6 in convert_inplace(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) /home/ubuntu/advancecomp-2.1/repng.cc:193
    #7 0x4088c6 in rezip_single(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, unsigned long long&, unsigned long long&) /home/ubuntu/advancecomp-2.1/repng.cc:283
    #8 0x4096c1 in rezip_all(int, char**) /home/ubuntu/advancecomp-2.1/repng.cc:317
    #9 0x40d5c6 in process(int, char**) /home/ubuntu/advancecomp-2.1/repng.cc:476
    #10 0x4034a4 in main /home/ubuntu/advancecomp-2.1/repng.cc:489
    #11 0x7f5e2d5ff82f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
    #12 0x404a28 in _start (/home/ubuntu/advpng-asan+0x404a28)

0x6190000068a0 is located 0 bytes to the right of 1056-byte region [0x619000006480,0x6190000068a0)
allocated by thread T0 here:
    #0 0x7f5e2e4fc602 in malloc (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x98602)
    #1 0x43e94f in adv_png_read_ihdr lib/png.c:723

SUMMARY: AddressSanitizer: heap-buffer-overflow ??:0 __asan_memcpy
Shadow bytes around the buggy address:
  0x0c327fff8cc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c327fff8cd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c327fff8ce0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c327fff8cf0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c327fff8d00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0c327fff8d10: 00 00 00 00[fa]fa fa fa fa fa fa fa fa fa fa fa
  0x0c327fff8d20: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c327fff8d30: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c327fff8d40: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c327fff8d50: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c327fff8d60: 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
==11954==ABORTING

After debugging, I found improper size of the png is the reason.

For example, if I modify the size of a png to -1 x 1056, 4-bit colormap, with advpng running, the function png_compress() gets a set of arguments as
#1 0x0000000000405a31 in png_compress (level=..., out_ptr=..., out_size=@0x7fffffffd93c: 32767, img_ptr=0x662361 '\001' <repeats 32 times>, img_scanline=1, img_pixel=1, x=0, y=0, dx=4294967295, dy=1056) at pngex.cc:55,
where dx equals the width of the png, dy equals the hight of the png and img_pixel depends on the bit depth.
As a result, the following code goes as:

# pngex.cc
    33  void png_compress(shrink_t level, data_ptr& out_ptr, unsigned& out_size, const unsigned char* img_ptr, unsigned img_scanline, unsigned img_pixel, unsigned x, unsigned y, unsigned dx, unsigned dy)
    34  {
    35          data_ptr fil_ptr;
    36          unsigned fil_size;
    37          unsigned fil_scanline;
    38          data_ptr z_ptr;
    39          unsigned z_size;
    40          unsigned i;
    41          unsigned char* p0;
    42
    43          fil_scanline = dx * img_pixel + 1;          # fil_scanline = 0xffffffff * 1 + 1 = 0, interger overflow happens
    44          fil_size = dy * fil_scanline;               # fil_size = 1056 * 0 = 0 
    45          z_size = oversize_zlib(fil_size);           
    46
    47          fil_ptr = data_alloc(fil_size);             # malloc(0), align to 0x10
    48          z_ptr = data_alloc(z_size);
    49
    50          p0 = fil_ptr;
    51
    52          for(i=0;i<dy;++i) {
    53                  const unsigned char* p1 = &img_ptr[x * img_pixel + (i+y) * img_scanline];
    54                  *p0++ = 0;
    55                  memcpy(p0, p1, dx * img_pixel);    # dx * img_pixel = 0xffffffff, while size of p0 is 0x10, buffer overflow happens.

e-mail: aboultraman@gmail.com

Discoverer: M4x@Chaitin Security Research Lab

1 Attachments

Discussion

  • Andrea Mazzoleni

    It's fixed in the git repository

    Thanks for reporting!

     
  • Andrea Mazzoleni

    • status: open --> closed-fixed
     
  • Jericho

    Jericho - 2019-03-07

    Can you link to the fixing commit please?

     
  • raz ben jehuda

    raz ben jehuda - 2019-07-29

    Hey.
    How did you create the erronous picture ? I want to create other permutations as an input.

     

Log in to post a comment.