Bug 2750 - There is a heap buffer overflow in the pal2rgb tool of the tiff library. A crafted TIFF document can lead to a heap based buffer overflow.
: There is a heap buffer overflow in the pal2rgb tool of the tiff library. A c...
Status: RESOLVED LATER
: libtiff
default
: unspecified
: All Linux
: P1 blocker
: ---
Assigned To:
:
:
: migrated_to_gitlab
:
:
  Show dependency treegraph
 
Reported: 2017-11-29 00:50 by
Modified: 2019-10-01 14:20 (History)


Attachments
heap overflow PoC (9.44 KB, image/tiff)
2017-11-29 02:28, jungun.baek
Details


Note

You need to log in before you can comment on or make changes to this bug.


Description From 2017-11-29 00:50:42
The vulnerability is triggered by ./tools/pal2rgb $FILE /dev/null

The asan debug information is below:

TIFFFetchNormalTag: Warning, IO error during reading of "XResolution"; tag
ignored.
TIFFFetchNormalTag: Warning, IO error during reading of "YResolution"; tag
ignored.
sample.tiff: JPEG compression support is not configured.
TIFFSetField: /dev/null: Unknown pseudo-tag 65537.
TIFFSetField: /dev/null: Unknown pseudo-tag 65538.
sample.tiff: JPEG compression support is not configured.
=================================================================
==29649==ERROR: AddressSanitizer: heap-buffer-overflow on address
0x611000009fe1 at pc 0x0000004f3109 bp 0x7fff697434d0 sp 0x7fff697434c8
WRITE of size 1 at 0x611000009fe1 thread T0
    #0 0x4f3108  (/home/vagrant/targets/asan/tt/tools/pal2rgb+0x4f3108)
    #1 0x7f678dc0cf44  (/lib/x86_64-linux-gnu/libc.so.6+0x21f44)
    #2 0x419ba5  (/home/vagrant/targets/asan/tt/tools/pal2rgb+0x419ba5)

0x611000009fe1 is located 0 bytes to the right of 225-byte region
[0x611000009f00,0x611000009fe1)
allocated by thread T0 here:
    #0 0x4c3f08  (/home/vagrant/targets/asan/tt/tools/pal2rgb+0x4c3f08)
    #1 0x4f2748  (/home/vagrant/targets/asan/tt/tools/pal2rgb+0x4f2748)
    #2 0x7f678dc0cf44  (/lib/x86_64-linux-gnu/libc.so.6+0x21f44)

SUMMARY: AddressSanitizer: heap-buffer-overflow
(/home/vagrant/targets/asan/tt/tools/pal2rgb+0x4f3108)
Shadow bytes around the buggy address:
  0x0c227fff93a0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c227fff93b0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c227fff93c0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c227fff93d0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c227fff93e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0c227fff93f0: 00 00 00 00 00 00 00 00 00 00 00 00[01]fa fa fa
  0x0c227fff9400: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c227fff9410: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c227fff9420: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c227fff9430: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c227fff9440: 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
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==29649==ABORTING

pal2rgb allocates the output buffer(obuf) too small at tools/pal2rgb.c:188.
That cause heap overflow and lead to memory corruption in TIFFSetupStrips().

(gdb) r sample.tiff /dev/null
The program being debugged has been started already.
Start it from the beginning? (y or n) y

Starting program: /home/vagrant/targets/asan/tt/tools/pal2rgb sample.tiff
/dev/null
TIFFFetchNormalTag: Warning, IO error during reading of "XResolution"; tag
ignored.
TIFFFetchNormalTag: Warning, IO error during reading of "YResolution"; tag
ignored.
sample.tiff: JPEG compression support is not configured.
TIFFSetField: /dev/null: Unknown pseudo-tag 65537.
TIFFSetField: /dev/null: Unknown pseudo-tag 65538.
sample.tiff: JPEG compression support is not configured.
*** Error in `/home/vagrant/targets/asan/tt/tools/pal2rgb': malloc(): memory
corruption: 0x0000000000652160 ***

Program received signal SIGABRT, Aborted.
0x00007ffff7741c37 in __GI_raise (sig=sig@entry=6) at
../nptl/sysdeps/unix/sysv/linux/raise.c:56
56    ../nptl/sysdeps/unix/sysv/linux/raise.c: No such file or directory.
(gdb) bt
#0  0x00007ffff7741c37 in __GI_raise (sig=sig@entry=6) at
../nptl/sysdeps/unix/sysv/linux/raise.c:56
#1  0x00007ffff7745028 in __GI_abort () at abort.c:89
#2  0x00007ffff777e2a4 in __libc_message (do_abort=1,
    fmt=fmt@entry=0x7ffff7890310 "*** Error in `%s': %s: 0x%s ***\n")
    at ../sysdeps/posix/libc_fatal.c:175
#3  0x00007ffff778c584 in malloc_printerr (ptr=0x652160,
    str=0x7ffff788c4df "malloc(): memory corruption", action=<optimized out>)
at malloc.c:4998
#4  _int_malloc (av=0x7ffff7acd760 <main_arena>, bytes=24) at malloc.c:3449
#5  0x00007ffff778dae0 in __GI___libc_malloc (bytes=24) at malloc.c:2893
#6  0x00000000004122a5 in TIFFSetupStrips (tif=tif@entry=0x651b80) at
tif_write.c:545
#7  0x00000000004124b5 in TIFFWriteCheck (tif=0x651b80, tiles=<optimized out>,
    module=0x42de50 <module> "TIFFWriteScanline") at tif_write.c:613
#8  0x0000000000412a74 in TIFFWriteScanline (tif=tif@entry=0x651b80,
buf=buf@entry=0x652070,
    row=row@entry=0, sample=sample@entry=0) at tif_write.c:56
#9  0x0000000000401dbd in main (argc=<optimized out>, argv=<optimized out>) at
pal2rgb.c:200

Affected version: 4.0.9
Latest version: 4.0.9
------- Comment #1 From 2017-11-29 01:44:26 -------
pal2rgb allocate output buffer to convert TIFF format. The output buffer always
uses a fixed size(225).  However, when write data to output buffer, imagewidth
and imagelength of the input TIFF file are used. it lead to heap overflow.

1) get imagewidth and imagelength from input TIFF

tools/pal2rgb.c:142     TIFFGetField(in, TIFFTAG_IMAGEWIDTH, &imagewidth);
tools/pal2rgb.c:143     TIFFGetField(in, TIFFTAG_IMAGELENGTH, &imagelength);

2) allocate output buffer
tools/pal2rgb.c:188       obuf = (unsigned
char*)_TIFFmalloc(TIFFScanlineSize(out)); // TIFFScanlineSize(out) returns
always 225.

3) write data to output buffer
tools/pal2rgb.c:191         for (row = 0; row < imagelength; row++) {
tools/pal2rgb.c:192             if (!TIFFReadScanline(in, ibuf, row, 0))
tools/pal2rgb.c:193                 goto done;
tools/pal2rgb.c:194             pp = obuf;
tools/pal2rgb.c:195             for (x = 0; x < imagewidth; x++) { // HEAP
OVERFLOW!
tools/pal2rgb.c:196                 *pp++ = (unsigned char) rmap[ibuf[x]];
tools/pal2rgb.c:197                 *pp++ = (unsigned char) gmap[ibuf[x]];
tools/pal2rgb.c:198                 *pp++ = (unsigned char) bmap[ibuf[x]];
tools/pal2rgb.c:199             }
tools/pal2rgb.c:200             if (!TIFFWriteScanline(out, obuf, row, 0)) //
CRASH!
tools/pal2rgb.c:201                 goto done;
tools/pal2rgb.c:202         }
------- Comment #2 From 2017-11-29 02:28:44 -------
Created an attachment (id=819) [details]
heap overflow PoC
------- Comment #3 From 2017-11-29 06:33:21 -------
The process's heap memory was corrupted by COLORMAP/mage Width/Image Length of
the TIFF document. The offset of the corresponding values in the poc.tiff file
is shown below.

COLORMAP {
    R : 0x25~0x224
    G : 0x225~0x424
    B : 0x425~0x624
}

Image Width : 0x24FE
Image Length : 0x250A
------- Comment #4 From 2017-12-07 04:42:35 -------
The following change fixes the describe behavior:

diff --git a/tools/pal2rgb.c b/tools/pal2rgb.c
index 7a57800..92d9e29 100644
--- a/tools/pal2rgb.c
+++ b/tools/pal2rgb.c
@@ -184,15 +184,17 @@ main(int argc, char* argv[])
     { unsigned char *ibuf, *obuf;
       register unsigned char* pp;
       register uint32 x;
-      ibuf = (unsigned char*)_TIFFmalloc(TIFFScanlineSize(in));
-      obuf = (unsigned char*)_TIFFmalloc(TIFFScanlineSize(out));
+      size_t ibuf_size = TIFFScanlineSize(in);
+      size_t obuf_size = TIFFScanlineSize(out);
+      ibuf = (unsigned char*)_TIFFmalloc(ibuf_size);
+      obuf = (unsigned char*)_TIFFmalloc(obuf_size);
       switch (config) {
       case PLANARCONFIG_CONTIG:
         for (row = 0; row < imagelength; row++) {
             if (!TIFFReadScanline(in, ibuf, row, 0))
                 goto done;
             pp = obuf;
-            for (x = 0; x < imagewidth; x++) {
+            for (x = 0; x < imagewidth && x < (obuf_size-3); x++) {
                 *pp++ = (unsigned char) rmap[ibuf[x]];
                 *pp++ = (unsigned char) gmap[ibuf[x]];
                 *pp++ = (unsigned char) bmap[ibuf[x]];
@@ -205,15 +207,15 @@ main(int argc, char* argv[])
         for (row = 0; row < imagelength; row++) {
             if (!TIFFReadScanline(in, ibuf, row, 0))
                 goto done;
-            for (pp = obuf, x = 0; x < imagewidth; x++)
+            for (pp = obuf, x = 0; x < imagewidth && x < obuf_size; x++)
                 *pp++ = (unsigned char) rmap[ibuf[x]];
             if (!TIFFWriteScanline(out, obuf, row, 0))
                 goto done;
-            for (pp = obuf, x = 0; x < imagewidth; x++)
+            for (pp = obuf, x = 0; x < imagewidth && x < obuf_size; x++)
                 *pp++ = (unsigned char) gmap[ibuf[x]];
             if (!TIFFWriteScanline(out, obuf, row, 0))
                 goto done;
-            for (pp = obuf, x = 0; x < imagewidth; x++)
+            for (pp = obuf, x = 0; x < imagewidth && x < obuf_size; x++)
                 *pp++ = (unsigned char) bmap[ibuf[x]];
             if (!TIFFWriteScanline(out, obuf, row, 0))
                 goto done;


But the pal2rgb stil segfaults. new backtrace:

Core was generated by `pal2rgb /home/ncopa/Downloads/poc.tiff out.tiff'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x000065eb6f74f534 in jpeg_abort () from /usr/lib/libjpeg.so.8
(gdb) bt
#0  0x000065eb6f74f534 in jpeg_abort () from /usr/lib/libjpeg.so.8
#1  0x000065eb6f9c26b7 in TIFFjpeg_abort (sp=sp@entry=0xdbd8738e7c0)
    at tif_jpeg.c:416
#2  0x000065eb6f9c3f8c in JPEGPreDecode (tif=0x65eb6fc06900, s=<optimized out>)
    at tif_jpeg.c:1114
#3  0x000065eb6f9d4305 in TIFFSeek (sample=0, row=54, tif=0x65eb6fc06900)
    at tif_read.c:379
#4  TIFFReadScanline (tif=0x65eb6fc06900, buf=0xdbd8738ef40, row=54, 
    sample=<optimized out>) at tif_read.c:446
#5  0x00000dbd8718c5fc in main (argc=<optimized out>, argv=<optimized out>)
    at pal2rgb.c:194
------- Comment #5 From 2018-01-18 13:37:36 -------
OK, so I don't know anything about TIFF, but reading the code it's apparent
that pal2rgb expects the output buffer to be 3x the size of the input buffer
(presumably it's taking an array of colors which are indices into a palette and
exploding them out into RGB tuples). For whatever reason, when given the POC
image it does not create an output buffer of the correct size.

Here's a patch that will detect the problem and reject the image:

diff --git a/tools/pal2rgb.c b/tools/pal2rgb.c
index 0423598f..4eb5e86f 100644
--- a/tools/pal2rgb.c
+++ b/tools/pal2rgb.c
@@ -182,8 +182,19 @@ main(int argc, char* argv[])
        { unsigned char *ibuf, *obuf;
          register unsigned char* pp;
          register uint32 x;
-         ibuf = (unsigned char*)_TIFFmalloc(TIFFScanlineSize(in));
-         obuf = (unsigned char*)_TIFFmalloc(TIFFScanlineSize(out));
+      uint64_t tss_in = TIFFScanlineSize(in);
+      uint64_t tss_out = TIFFScanlineSize(out);
+      if (tss_out / tss_in < 3) {
+          /*
+           * BUG 2750: The following code assumes the output buffer is 3x the
+           * length of the input buffer due to exploding the palette into
+           * RGB tuples. If this doesn't happen, fail now.
+           */
+          fprintf(stderr, "Could not determine correct image size for output.
Exiting.\n");
+          return -1;
+      }
+         ibuf = (unsigned char*)_TIFFmalloc(tss_in);
+         obuf = (unsigned char*)_TIFFmalloc(tss_out);
          switch (config) {
          case PLANARCONFIG_CONTIG:
                for (row = 0; row < imagelength; row++) {
------- Comment #6 From 2018-08-24 08:41:41 -------
Question is, why TIFFScanlineSize(out) returns less than 3*imagewidth, as the
rest of pal2rgb assume.

Breakpoint 2, main (argc=3, argv=0x7fffffffe8a8) at pal2rgb.c:187
187          ibuf = (unsigned char*)_TIFFmalloc(TIFFScanlineSize(in));
(gdb) call TIFFScanlineSize(in)
$7 = 150
(gdb) n
188          obuf = (unsigned char*)_TIFFmalloc(TIFFScanlineSize(out));
(gdb) call TIFFScanlineSize(out)
$8 = 225
(gdb)

Following code has the influence, because TIFFScanlineSize() value depends on
photometric content (TIFFTAG_PHOTOMETRIC):

        case COMPRESSION_JPEG:
                if (jpegcolormode == JPEGCOLORMODE_RGB)
                        photometric = PHOTOMETRIC_YCBCR;
                else
                        photometric = PHOTOMETRIC_RGB;
                TIFFSetField(out, TIFFTAG_JPEGQUALITY, quality);
                TIFFSetField(out, TIFFTAG_JPEGCOLORMODE, jpegcolormode);
                break;

When photometric is set to PHOTOMETRIC_YCBCR, the value of
TIFFScanlineSize(out) is 225 in this case. While set to PHOTOMETRIC_RGB, the
value is TIFFScanlineSize(out) is then 450, i. e. 3*TIFFScanlineSize(in), which
would suffice.
------- Comment #7 From 2019-10-01 14:20:48 -------
Bugzilla is no longer used for tracking libtiff issues. Remaining open tickets,
such as this one, have been migrated to the libtiff GitLab instance at
https://gitlab.com/libtiff/libtiff/issues .

The migrated tickets have their summary prefixed with [BZ#XXXX] where XXXX is
the initial Bugzilla issue number.