Security
Headlines
HeadlinesLatestCVEs

Headline

CVE-2021-40263: FreeImage / Bugs / #336 A heap_overflow on PluginTIFF.cpp when Load() TIFF

A heap overflow vulnerability in FreeImage 1.18.0 via the ofLoad function in PluginTIFF.cpp.

CVE
#vulnerability#windows
  • Summary
  • Files
  • Reviews
  • Support
  • Mailing Lists
  • Code
  • Tickets ▾
    • Feature Requests
    • Patches
    • Bugs
    • Support Requests
  • News
  • Discussion
  • FreeImage

Menu ▾ ▴

Status: open

Owner: nobody

Labels: None

Priority: 5

Updated: 2021-08-26

Created: 2021-08-26

Private: No

tl;dr:
When Load TIFF files, “ImageWidth” and “TileWidth” is read from file. FreeImage alloc memory size is controlled by "ImageWidth", but FreeImage write memory size if controlled by "TileWidth". if result of “TileWidth” diff with "ImageWidth", it will writing data more than the allocated memory.

When the program reads a TIFF file, it will be handed to the Load function of the ‘PluginTIFF.cpp’ file,
in Load function, we alloc memory by CreateImageType(), and write memory in Load function with "memcpy". And the "height", "width", "bitspersample", "samplesperpixel", “tileRowSize” and “imageRowSize” is read from file.

static FIBITMAP * DLL_CALLCONV Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { … uint32_t height = 0; uint32_t width = 0; uint16_t bitspersample = 1; uint16_t samplesperpixel = 1; … TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &width); TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &height); TIFFGetField(tif, TIFFTAG_SAMPLESPERPIXEL, &samplesperpixel); TIFFGetField(tif, TIFFTAG_BITSPERSAMPLE, &bitspersample); TIFFGetField(tif, TIFFTAG_ROWSPERSTRIP, &rowsperstrip);
TIFFGetField(tif, TIFFTAG_ICCPROFILE, &iccSize, &iccBuf); TIFFGetFieldDefaulted(tif, TIFFTAG_PLANARCONFIG, &planar_config); … // ---------------------------------------------------------------------------------

    if(loadMethod \== LoadAsRBGA) {

… } else if(loadMethod == LoadAsTiled) { // --------------------------------------------------------------------------------- // Tiled image loading // ---------------------------------------------------------------------------------

        uint32\_t tileWidth, tileHeight;
        uint32\_t src\_line \= 0;

        // create a new DIB                                                     v\-------- alloc memory there
        dib \= CreateImageType( header\_only, image\_type, width, height, bitspersample, samplesperpixel);

… // calculate src line and dst pitch unsigned dst_pitch = FreeImage_GetPitch(dib); uint32_t tileRowSize = (uint32_t)TIFFTileRowSize(tif); uint32_t imageRowSize = (uint32_t)TIFFScanlineSize(tif);

            // In the tiff file the lines are saved from up to down 
            // In a DIB the lines must be saved from down to up

            BYTE \*bits \= FreeImage\_GetScanLine(dib, height \- 1);

            for (uint32\_t y \= 0; y < height; y += tileHeight) {                 // <---- write memory there     
                int32\_t nrows \= (y + tileHeight \> height ? height \- y : tileHeight);                    

                for (uint32\_t x \= 0, rowSize \= 0; x < width; x += tileWidth, rowSize += tileRowSize) {
                    memset(tileBuffer, 0, tileSize);

                    // read one tile
                    if (TIFFReadTile(tif, tileBuffer, x, y, 0, 0) < 0) {
                        free(tileBuffer);
                        throw "Corrupted tiled TIFF file";
                    }
                    // 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, MIN(dst\_pitch, src\_line));
                        src\_bits += tileRowSize;
                        dst\_bits \-= dst\_pitch;
                    }
                }

                bits \-= nrows \* dst\_pitch;
            }

… }

CreateImageType() alloc memory by “width","height","bitspersample” and "samplesperpixel".

static FIBITMAP* CreateImageType(BOOL header_only, FREE_IMAGE_TYPE fit, int width, int height, uint16_t bitspersample, uint16_t samplesperpixel) { FIBITMAP *dib = NULL; … int bpp = bitspersample * samplesperpixel; //<---- bpp == bitspersample * samplesperpixel … dib = FreeImage_AllocateHeader(header_only, width, height, bpp, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); … return dib; }

FIBITMAP * DLL_CALLCONV FreeImage_AllocateHeader(BOOL header_only, int width, int height, int bpp, unsigned red_mask, unsigned green_mask, unsigned blue_mask) { return FreeImage_AllocateBitmap(header_only, NULL, 0, FIT_BITMAP, width, height, bpp, red_mask, green_mask, blue_mask); }

static FIBITMAP * FreeImage_AllocateBitmap(BOOL header_only, BYTE *ext_bits, unsigned ext_pitch, FREE_IMAGE_TYPE type, int width, int height, int bpp, unsigned red_mask, unsigned green_mask, unsigned blue_mask) { …

    size\_t dib\_size \= FreeImage\_GetInternalImageSize(header\_only || ext\_bits, width, height, bpp, need\_masks);      //<---- calc alloc size by bpp, width and height

    if(dib\_size \== 0) {
        // memory allocation will fail (probably a malloc overflow)
        free(bitmap);
        return NULL;
    }

    bitmap\->data \= (BYTE \*)FreeImage\_Aligned\_Malloc(dib\_size \* sizeof(BYTE), FIBITMAP\_ALIGNMENT);   //<---- real alloc function

… }

in FreeImage_GetInternalImageSize(), it calc alloc size

static size_t FreeImage_GetInternalImageSize(BOOL header_only, unsigned width, unsigned height, unsigned bpp, BOOL need_masks) { size_t dib_size = sizeof(FREEIMAGEHEADER); … if(!header_only) { const size_t header_size = dib_size;

// pixels are aligned on a 16 bytes boundary dib_size += (size_t)CalculatePitch(CalculateLine(width, bpp)) * (size_t)height; //<— alloc size by line lenght * height, line lenght calc by width and bpp( = bitspersample * samplesperpixel) … }

return dib_size; }

but when we write memory, the read size calc by “tileWidth” and "width", loop (tileHeight//height) * (tileWidth//width) times.

            for (uint32\_t y \= 0; y < height; y += tileHeight) {                                            //<-- loop tileHeight//height times
                int32\_t nrows \= (y + tileHeight \> height ? height \- y : tileHeight);                    

                for (uint32\_t x \= 0, rowSize \= 0; x < width; x += tileWidth, rowSize += tileRowSize) { //<-- loop tileWidth//width times
                    memset(tileBuffer, 0, tileSize);

                    // read one tile
                    if (TIFFReadTile(tif, tileBuffer, x, y, 0, 0) < 0) {
                        free(tileBuffer);
                        throw "Corrupted tiled TIFF file";
                    }
                    // 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, MIN(dst\_pitch, src\_line));
                        src\_bits += tileRowSize;
                        dst\_bits \-= dst\_pitch;
                    }
                }

                bits \-= nrows \* dst\_pitch;
            }

Finally, it will cause the heap overflow if we make the result of “TileWidth” diff with "ImageWidth". Moreover, it may has the risk of arbitrary code execution.

windbg:

ModLoad: 10000000 105ee000 D:\temp\_zzz\FreeImage.dll ModLoad: 752b0000 752c4000 C:\Windows\SysWOW64\VCRUNTIME140.dll ModLoad: 755e0000 75643000 C:\Windows\SysWOW64\WS2_32.dll ModLoad: 77050000 7710f000 C:\Windows\SysWOW64\RPCRT4.dll (8b8c.710): Break instruction exception - code 80000003 (first chance) eax=00000000 ebx=00000000 ecx=dd680000 edx=00000000 esi=77442044 edi=7744260c eip=774e1b72 esp=006ff63c ebp=006ff668 iopl=0 nv up ei pl zr na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246 ntdll!LdrpDoDebuggerBreak+0x2b: 774e1b72 cc int 3 0:000> g (8b8c.710): Access violation - code c0000005 (first chance) First chance exceptions are reported before any exception handling. This exception may be expected and handled. *** WARNING: Unable to verify checksum for D:\temp\_zzz\FreeImage.dll eax=00000049 ebx=00000008 ecx=00000001 edx=00000001 esi=06667fd0 edi=06662000 eip=102a6e8f esp=006ff86c ebp=006ffa58 iopl=0 nv up ei pl nz na po nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010202 FreeImage!memcpy+0x51f: 102a6e8f 8807 mov byte ptr [edi],al ds:002b:06662000=?? 0:000> db edi-0x20 06661fe0 00 00 00 00 00 00 00 00-2a 2a 2a 2a 2a 2a 2a 2a …******** 06661ff0 49 49 49 49 49 49 49 49-49 49 49 49 49 49 49 49 IIIIIIIIIIIIIIII 06662000 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ??? 06662010 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ??? 06662020 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ??? 06662030 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ??? 06662040 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ??? 06662050 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ??? 0:000> kv # ChildEBP RetAddr Args to Child
00 006ff870 100378e8 06662000 06667fd0 00000001 FreeImage!memcpy+0x51f (FPO: [3,0,2]) (CONV: cdecl) [d:\agent\_work\4\s\src\vctools\crt\vcruntime\src\string\i386\memcpy.asm @ 669] 01 006ffabc 1000d9de 006ffb10 06619fc8 06667fd0 FreeImage!_TIFFmemcmp+0x2d28 (FPO: [5,139,3]) 02 006ffaf0 1000da60 00000012 006ffb10 06619fc8 FreeImage!FreeImage_LoadFromHandle+0x8e (FPO: [Uses EBP] [4,3,2]) 03 006ffb1c 00b0107b 00000012 06615fee 00000000 FreeImage!FreeImage_Load+0x50 (FPO: [3,4,2])

TestFile:

data:image/tiff;base64,SUkqAAgAAAAGAAABAwABAAAAMAAwMAEBAwABAAAAMAAwMBcBBAAYAAAACAAAABEBAwABAAAAMAABABYBAwABAAAAMAABAEIBAwABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=

2 Attachments

Discussion

Log in to post a comment.

CVE: Latest News

CVE-2023-50976: Transactions API Authorization by oleiman · Pull Request #14969 · redpanda-data/redpanda
CVE-2023-6905
CVE-2023-6903
CVE-2023-6904
CVE-2023-3907