Security
Headlines
HeadlinesLatestCVEs

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.

CVE
#windows#js
  • 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.

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