Menu

Three vulnerabilities about freeimage3.18

Developers
taolaw
2019-05-19
2020-07-06
  • taolaw

    taolaw - 2019-05-19

    1.FreeImage-3.18 LibOpenJpeg/j2k.c file memcpy function Out-of-bounds

    A out-of-bounds in line '3643' of the 'j2k_read_ppm_v3' function. Where the value of 'l_N_ppm' comes from the file read in, and occurs out-of-bounds when 'l_N_ppm' is greater than the size of p_header_data.
    1

    2.FreeImage-3.18 PluginTIFF.cpp file load function heap overflow vulnerability

    When the program reads a tiff file, it will be handed to the Load function of the 'PluginTIFF.cpp' file, but in the '2074' line of the program, when the 'memcpy' function is executed, the destination address and the size of the copied data are not considered, resulting in heap overflow.
    2
    In the code above, we can see that 'dst_bits' comes from 'bits+rowSize' , In first round of the loop , the 'rowSize' is 0 , so the value of 'dst_bits' is the bits, and the bits is the return pointer of the 'FreeImage_GetScanLine' function
    3
    4
    When the 'height' parameter is 1 , we see that the actual returned pointer is the return value of the 'FreeImage_GetBits' function
    5
    However,when I look at the documentation and the code of the 'FreeImage_GetBits' function, I still don't find that the returned pointer has a complete stack structure. Which makes dst_bits is like a magic address.
    In fact, it is found through debugging that this address is in the heap space , but when the src_line is large enough, it will cause the coverage of the adjacent heap.
    6
    The sample of the crash is in the attachment, the name is heap-buff-overflow.tiff

    3.A stack buff overflower on line 1284 at PluginTIFF.cpp

    When reading a tiff file, the program will call the load function in 'PluginTIFF.cpp', In the '2251' line of the 'load' function, the program will call the 'ReadThumbnail' function.
    7
    Entering the 'ReadThumbnail' function, we see that the 'load' function is called again on line '1288'. However, the decision to determine the recursion is based on the return value of the 'TIFFSetSubDirectory' function.
    8
    In fact, the function that determines the return value of TIFFSetSubDirectory is the result of the 'TIFFReadDirectory' function.But under some special conditions, the 'TIFFReadDirectory' function always returns 1, which will cause the program stack space to be filled.
    9
    The following is a concrete example, which causes program memory corruption, which can lead to remote denial of service by attackers.
    10
    The sample of the crash is in the attachment, the name is stack-overflow.tiff

     

    Last edit: taolaw 2019-05-19
  • taolaw

    taolaw - 2019-05-20

    4.A stack buff overflower in JXRMeta.c

    When reading a special JXR file, the 249 line StreamCalcIFDSize function of JXRMeta.c repeatedly calls itself due to improper processing of the file, eventually causing the stack to be filled.An attacker can reach a remote denial of service attack by sending a specially constructed file.

    The following example is the status of the stack space after the crash is triggered.
    11

     
  • Hugo Lefeuvre

    Hugo Lefeuvre - 2019-11-03

    Regarding overflow No. 2.

    From the Debian bug report:

    The overflow happens during the following call to memcpy:

    // convert to strip
    if(x + tileWidth > width) {
            src_line = imageRowSize - rowSize;
    } else {
            src_line = tileRowSize;
    }
    BYTE *src_bits = tileBuffer;
    BYTE *dst_bits = bits + rowSize;
    for(int k = 0; k < nrows; k++) {
            memcpy(dst_bits, src_bits, src_line);
            src_bits += tileRowSize;
            dst_bits -= dst_pitch;
    }
    

    This portion of code copies image data from a libTIFF-provided buffer to an
    internal buffer. The overflow happens because src_line is larger than the
    size of dst_bits.

    This is the result of an inconsistency between libTIFF and freeimage:

    In the libTIFF case, tile row size is
    = samplesperpixel * bitspersample * tilewidth / 8
    = bitsperpixel * tilewidth / 8
    = 6 * 32 * 7 / 8 = 168

    In the freeimage case, tile row size is
    bitsperpixel * tilewidth / 8
    = 32 * 7 / 8 = 28

    As a result, the two buffers are differently sized.

    freeimage has a bpp of 32 because CreateImageType calls
    FreeImage_AllocateHeader with MIN(bpp, 32).

    This 'MIN(bpp, 32)' looks like a terrible hack to me, but we can't change
    it to 'bpp' because FIT_BITMAP images with bpp > 32 does not seem to be
    supported by freeimage. Also, in this case, bpp > 32 doesn't even make
    sense:

    Looking closely at the reproducer, we can notice that it defines a bilevel
    image with samplesperpixel and bitspersample parameters, both unexpected in
    bilevel images.

    Pixels in bilevel images can either be black or white. There is as such
    only one sample per pixel, and a single bit per sample is sufficient. The
    spec defines bpp = 8. It is unclear whether the specification allows for
    arbitrary values of bitspersample or samplesperpixel (extrasamples?) in
    this case.

    This file gets rejected by most libTIFF tools.

    patch

    • add check to CreateImageType() to reject FIT_BITMAP images with bpp > 32
      instead of passing MIN(bpp, 32).
    • change type of dst_pitch to unsigned
    • call memcpy with MIN(dst_pitch, src_line) instead of src_line. this will
      help overcome any further (future) discrepancy between libTIFF and
      freeimage.

    You can find the patch in attachment.

     

    Last edit: Hugo Lefeuvre 2019-11-03
    • owen

      owen - 2020-07-06

      I seem to have run into this issue with freeimage not being able to handle bpp over 32;
      I have a dng photo that I try to convert to grayscale. I can provide a sample dng photo that seems to cause freeimage to fail.

      I created a mailing list issue but no replies as of yet:

       FIBITMAP* fi_bitmap = FreeImage_Load(format, path);
        if (fi_bitmap == nullptr) {
          return false;
        }
      
       int bpp = FreeImage_GetBPP(fi_bitmap);
      this returns 48
      
      FIBITMAP* gs_bitmap = FreeImage_ConvertToGreyscale(fi_bitmap);
      
      bpp = FreeImage_GetBPP(gs_bitmap); //at this point it returns 0
      

      Why is this call failing? I am working with an adobe dng file.

       
  • Natanael Copa

    Natanael Copa - 2020-05-19

    Aret here any fixes for CVE-2019-12212 and CVE-2019-12214?

     

Log in to post a comment.