Skip to content

Heap buffer overflow(read) in _cfImageReadTIFF #116

@IliaKash1

Description

@IliaKash1

Hello! I have found a heap buffer overflow in image-tiff.c

It does not occur in the project itself, but rather is caused by it. Specifically, the line 545

TIFFReadScanline(tif, in, row, 0);

With a specific TIFF file in is allocated with size img->xsize * 3 + 3 (294 bytes here). Due to the call to TIFFReadScanline() libtiff writes TIFFScanlineSize(tif) (scanwidth, 388 here, which matches width * samples) bytes to in and that overruns in by 94 bytes, which in turn results in a crash in zlib (inflate() -> updatewindow()).

I don't think it is exploitable in any way, so I am writing this to the issues tab, not the security tab.

Output:

TIFFReadDirectoryCheckOrder: Warning, Invalid TIFF directory; tags are not sorted in ascending order.
TIFFReadDirectory: Warning, Incorrect count for "ColorMap"; tag ignored.
TIFFReadDirectory: Warning, Sum of Photometric type-related color channels and ExtraSamples doesn't match SamplesPerPixel. Defining non-color channels as ExtraSamples..
=================================================================
==108589==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x512000000466 at pc 0x6336adcdd9a4 bp 0x7ffe80349220 sp 0x7ffe803489d8
READ of size 388 at 0x512000000466 thread T0
    #0 0x6336adcdd9a3 in memcpy (/orig/libcupsfilters/overflow+0xcb9a3) (BuildId: 87fb0c8d052c1d70bc771abaced78e38ad6d0aac)
    #1 0x7f33470db9da in updatewindow /orig_zlib/zlib/inflate.c:282:9
    #2 0x7f33470db6c5 in inflate /orig_zlib/zlib/inflate.c:1135:13
    #3 0x7f3347bf1671 in ZIPDecode /tiff/libtiff/tif_zip.c:259:17
    #4 0x7f3347be8588 in TIFFReadScanline /tiff/libtiff/tif_read.c:458:14
    #5 0x6336add296d1 in _cfImageReadTIFF /orig/libcupsfilters/cupsfilters/image-tiff.c:545:15
    #6 0x6336add20fe6 in cfImageOpenFP /orig/libcupsfilters/cupsfilters/image.c:409:14
    #7 0x6336add1eeb1 in main /orig/libcupsfilters/overflow.c:22:5
    #8 0x7f33473aa249 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
    #9 0x7f33473aa304 in __libc_start_main csu/../csu/libc-start.c:360:3
    #10 0x6336adc3f700 in _start (/orig/libcupsfilters/overflow+0x2d700) (BuildId: 87fb0c8d052c1d70bc771abaced78e38ad6d0aac)

0x512000000466 is located 0 bytes after 294-byte region [0x512000000340,0x512000000466)
allocated by thread T0 here:
    #0 0x6336adcdeb69 in calloc (/orig/libcupsfilters/overflow+0xccb69) (BuildId: 87fb0c8d052c1d70bc771abaced78e38ad6d0aac)
    #1 0x6336add28447 in _cfImageReadTIFF /orig/libcupsfilters/cupsfilters/image-tiff.c:367:26
    #2 0x6336add20fe6 in cfImageOpenFP /orig/libcupsfilters/cupsfilters/image.c:409:14
    #3 0x6336add1eeb1 in main /orig/libcupsfilters/overflow.c:22:5
    #4 0x7f33473aa249 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16

SUMMARY: AddressSanitizer: heap-buffer-overflow (/orig/libcupsfilters/overflow+0xcb9a3) (BuildId: 87fb0c8d052c1d70bc771abaced78e38ad6d0aac) in memcpy
Shadow bytes around the buggy address:
  0x512000000180: fa fa fa fa fa fa fa fa 00 00 00 00 00 00 00 00
  0x512000000200: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x512000000280: 00 00 00 00 00 00 00 00 00 00 00 00 fa fa fa fa
  0x512000000300: fa fa fa fa fa fa fa fa 00 00 00 00 00 00 00 00
  0x512000000380: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x512000000400: 00 00 00 00 00 00 00 00 00 00 00 00[06]fa fa fa
  0x512000000480: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x512000000500: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x512000000580: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x512000000600: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x512000000680: 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
==108589==ABORTING

Steps to reproduce:

  1. Build the project with sanitizers:
export CFLAGS="-g -O0 -fsanitize=address"
export CXXFLAGS="-g -O0 -fsanitize=address"
export CC=clang
export CXX=clang++

autoreconf -fi
./configure --enable-static --disable-silent-rules --with-mutool-path=/usr/bin/mutool --enable-mutool
make
  1. Build the example:
$CC $CFLAGS -O0 -o overflow overflow.c ./.libs/libcupsfilters.a -I./ -lcups -ltiff -ljpeg -lpng -lexif -lm -ldl -ljxl

overflow.c:

#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <cupsfilters/filter.h>
#include <cupsfilters/raster.h>
#include <cupsfilters/colormanager.h>
#include <cupsfilters/ipp.h>
#include <cupsfilters/image-private.h>
#include <cupsfilters/libcups2-private.h>

extern char *
fparseln(FILE *fp, size_t *size, size_t *lineno, const char str[3], int flags);


int main(int argc, char** argv)
{
    if (argc < 2) return 0;
    FILE *fp = fopen(argv[1], "r");
    if (!fp) return 0;
    cfImageOpenFP(fp, CF_IMAGE_BLACK, CF_IMAGE_BLACK, 100, 0, NULL);
    return 0;
}
  1. Launch the example with the provided input:

hbo_input.zip

./overflow hbo_input

Suggested workaround:

I don't know if ensuring that in buffer is big enough is the correct approach, so no PR this time.

diff --git a/cupsfilters/image-tiff.c b/cupsfilters/image-tiff.c
index 02aa4b76..49c0fb40 100644
--- a/cupsfilters/image-tiff.c
+++ b/cupsfilters/image-tiff.c
@@ -363,8 +363,11 @@ _cfImageReadTIFF(
       pstep = xdir * 3;
     else
       pstep = xdir;
-
-    if ((in  = (cf_ib_t*)calloc(img->xsize * 3 + 3, sizeof(cf_ib_t))) == NULL)
+    int inbytes = img->xsize * 3 + 3;
+    if (scanwidth > inbytes)
+      inbytes  = scanwidth;
+    in = (cf_ib_t*)calloc(inbytes, sizeof(cf_ib_t));
+    if (in == NULL)
     {
       DEBUG_puts("DEBUG: No enough memory.\n");
       TIFFClose(tif);
@@ -386,8 +389,11 @@ _cfImageReadTIFF(
       pstep = ydir * 3;
     else
       pstep = ydir;
-
-    if ((in  = (cf_ib_t*)calloc(img->ysize * 3 + 3, sizeof(cf_ib_t))) == NULL)
+    int inbytes = img->ysize * 3 + 3;
+    if (scanwidth > inbytes)
+      inbytes  = scanwidth;
+    in = (cf_ib_t*)calloc(inbytes, sizeof(cf_ib_t));
+    if (in == NULL)
     {
       DEBUG_puts("DEBUG: No enough memory.\n");
       TIFFClose(tif);

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions