Headline
CVE-2021-40265: FreeImage / Bugs / #337 A heap_overflow on PluginJPEG.cpp when Load() SOF(Start Of Frame) JPEG
A heap overflow bug exists FreeImage before 1.18.0 via ofLoad function in PluginJPEG.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 SOF(Start Of Frame) JPEG files, “components” of SOF is read from file. FreeImage alloc memory size is controlled by “components” , but sometime FreeImage replace default size to alloc. if “components” is greater than default in alloc function, it will writing data more than the allocated memory.
When the program reads a JPEG file, it will be handed to the Load function of the ‘PluginJPEG.cpp’ file,
in Load function, we alloc memory by FreeImage_AllocateHeader(), and write memory in jpeg_read_scanlines(). And the “components” is read from file.
static FIBITMAP * DLL_CALLCONV Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { if (handle) { … struct jpeg_decompress_struct cinfo; … // step 3: read handle parameters with jpeg_read_header()
jpeg\_read\_header(&cinfo, TRUE); //<---- read components from file
… if((cinfo.output_components == 4) && (cinfo.out_color_space == JCS_CMYK)) { … } else { // RGB or greyscale image v------------ alloc memory function dib = FreeImage_AllocateHeader(header_only, cinfo.output_width, cinfo.output_height, 8 * cinfo.output_components, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); … } … if((cinfo.out_color_space == JCS_CMYK) && ((flags & JPEG_CMYK) != JPEG_CMYK)) { … } else if((cinfo.out_color_space == JCS_CMYK) && ((flags & JPEG_CMYK) == JPEG_CMYK)) { … } else { // normal case (RGB or greyscale image)
while (cinfo.output\_scanline < cinfo.output\_height) {
JSAMPROW dst \= FreeImage\_GetScanLine(dib, cinfo.output\_height \- cinfo.output\_scanline \- 1);
jpeg\_read\_scanlines(&cinfo, &dst, 1); // <---------- write memory
}
// step 7b: swap red and blue components (see LibJPEG/jmorecfg.h: #define RGB\_RED, ...)
// The default behavior of the JPEG library is kept "as is" because LibTIFF uses
// LibJPEG "as is".
… }
return NULL;
}
FreeImage_AllocateHeader() alloc memory by "output_components", but FreeImage_AllocateHeader function maybe replace default size with it.
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) { … // check pixel bit depth switch(type) { case FIT_BITMAP: switch(bpp) { case 1: case 4: case 8: break; case 16: need_masks = TRUE; break; case 24: case 32: break; default: bpp = 8; //<---- change bpp there break; } break; … size_t dib_size = FreeImage_GetInternalImageSize(header_only || ext_bits, width, height, bpp, need_masks); //<---- calc alloc size by bpp(num_components)
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(num\_components)
… }
return dib\_size;
}
but when we use jpeg_read_scanlines, the read size calc by "num_components", usually equal "output_components".
METHODDEF(void) null_convert (j_decompress_ptr cinfo, JSAMPIMAGE input_buf, JDIMENSION input_row, JSAMPARRAY output_buf, int num_rows) { register JSAMPROW outptr; register JSAMPROW inptr; register JDIMENSION count; register int num_comps = cinfo->num_components; JDIMENSION num_cols = cinfo->output_width; int ci;
while (–num_rows >= 0) { /* It seems fastest to make a separate pass for each component. */ for (ci = 0; ci < num_comps; ci++) { inptr = input_buf[ci][input_row]; outptr = output_buf[0] + ci; for (count = num_cols; count > 0; count–) { *outptr = *inptr++; /* don’t need GETJSAMPLE() here */ //<---- copy size calc by width(output_width) and bpp(num_components) outptr += num_comps; } } input_row++; output_buf++; } }
callstack
FreeImage.dll!null_convert(jpeg_decompress_struct * cinfo, unsigned char * * * input_buf, unsigned int input_row, unsigned char * * output_buf, int num_rows) FreeImage.dll!sep_upsample(jpeg_decompress_struct * cinfo, unsigned char * * * input_buf, unsigned int * in_row_group_ctr, unsigned int in_row_groups_avail, unsigned char * * output_buf, unsigned int * out_row_ctr, unsigned int out_rows_avail) FreeImage.dll!process_data_simple_main(jpeg_decompress_struct * cinfo, unsigned char * * output_buf, unsigned int * out_row_ctr, unsigned int out_rows_avail) FreeImage.dll!jpeg_read_scanlines(jpeg_decompress_struct * cinfo, unsigned char * * scanlines, unsigned int max_lines) FreeImage.dll!Load(FreeImageIO * io, void * handle, int page, int flags, void * data) FreeImage.dll!FreeImage_LoadFromHandle(FREE_IMAGE_FORMAT fif, FreeImageIO * io, void * handle, int flags) FreeImage.dll!FreeImage_Load(FREE_IMAGE_FORMAT fif, const char * filename, int flags)
Finally, it will cause the heap overflow if we make the “components” not in [1, 2, 3]. 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
(5cb0.349c): Break instruction exception - code 80000003 (first chance)
eax=00000000 ebx=00000000 ecx=608e0000 edx=00000000 esi=77442044 edi=7744260c
eip=774e1b72 esp=0133f5c8 ebp=0133f5f4 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
(5cb0.349c): 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=08539007 ebx=00000009 ecx=00000880 edx=073590c8 esi=00000721 edi=00000000
eip=1009aec5 esp=0133f6cc ebp=00000000 iopl=0 nv up ei pl nz na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010206
FreeImage!jinit_color_deconverter+0x935:
1009aec5 8808 mov byte ptr [eax],cl ds:002b:08539007=??
0:000> db eax-0x20
08538fe7 00 00 00 00 00 80 00 00-00 c0 c0 c0 c0 c0 80 c0 …
08538ff7 c0 c0 c0 c0 d0 d0 d0 80-d0 ?? ?? ?? ?? ?? ?? ?? …???
08539007 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ???
08539017 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ???
08539027 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ???
08539037 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ???
08539047 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ???
08539057 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ???
0:000> kv
# ChildEBP RetAddr Args to Child
00 0133f6d8 1009a383 00000808 073536cc 00000000 FreeImage!jinit_color_deconverter+0x935 (FPO: [Uses EBP] [5,0,1])
01 0133f704 100991d3 073536c0 07354fe0 07355008 FreeImage!jinit_upsampler+0x263 (FPO: [Uses EBP] [7,1,4])
02 0133f734 10085bfa 0133f830 0133fa18 0133f754 FreeImage!jinit_d_main_controller+0x1f3 (FPO: [Uses EBP] [4,0,4])
03 0133f74c 1002283f 00000000 0133fa18 00000001 FreeImage!jpeg_read_scanlines+0x8a (FPO: [3,0,1])
04 0133fa48 1000d9de 0133fa9c 07339fc8 ffffffff FreeImage!jpeg_freeimage_dst+0x1b1f (FPO: [5,183,3])
05 0133fa7c 1000da60 00000002 0133fa9c 07339fc8 FreeImage!FreeImage_LoadFromHandle+0x8e (FPO: [Uses EBP] [4,3,2])
06 0133faa8 00b0107b 00000002 07335fee 00000000 FreeImage!FreeImage_Load+0x50 (FPO: [3,4,2])
TestFile:
data:image/jpeg;base64,/9j/2wADAP/KACMICAgICAkBETLvESEDESH/QSF5IgAxETExFDExIjExETEx/9oACAF5AAAAAA==
2 Attachments
Discussion
Log in to post a comment.