Talos Vulnerability Report

TALOS-2016-0189

FreeImage Library XMP Image Handling Code Execution Vulnerability

October 3, 2016
CVE Number

CVE-2016-5684

Summary

An exploitable out-of-bounds write vulnerability exists in the XMP image handling functionality of the FreeImage library.

A specially crafted XMP file can cause an arbitrary memory overwrite resulting in code execution. An attacker can provide a malicious image to trigger this vulnerability.

Tested Versions

FreeImage 3.17.0

Product URLs

http://freeimage.sourceforge.net/

CVSSv3 Score

8.8 - CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H

Details

The FreeImage library is used by over 100+ programs according to http:// freeimage.sourceforge.net/users.html. Major consumers include the game engine Unity and Spamfighter, a major antispam filter developer.

Consumers of this library often identify image files by calling a generic loader function such as FreeImage_GetFileType() to get a file type using a signature, or using the extension with the function

FreeImageGetFIFFromFilename(). Once these have been called, the image is loaded using FreeImageLoad(). Since this often doesn't require a particular file format to be specified by the user of the library,

it allows for easy loading and support of multiple file formats. However, this also means that file formats that a particular program doesn't intend to support, might be parsed by FreeImage anyway, regardless of extension.

The vulnerability occurs in the file Source/FreeImage/PluginXPM.cpp in the function Load(), which is called when an XPM file is being loaded.

At lines 177-207 the following code is present:

int width, height, colors, cpp;

[178]if( sscanf(str, "%d %d %d %d", &width, &height, &colors, &cpp) != 4 ) {

    free(str);

    throw "Improperly formed info string";

}

free(str);



if (colors > 256) {

    dib = FreeImage_AllocateHeader(header_only, width, height, 24, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);

} else {

    dib = FreeImage_AllocateHeader(header_only, width, height, 8);

}



//build a map of color chars to rgb values

std::map<std::string,FILE_RGBA> rawpal; //will store index in Alpha if 8bpp

for(int i = 0; i < colors; i++ ) {

    FILE_RGBA rgba;



    str = ReadString(io, handle);

    if(!str)

        throw "Error reading color strings";



    std::string chrs(str,cpp); //create a string for the color chars using the first cpp chars

    [200]char *keys = str + cpp; //the color keys for these chars start after the first cpp chars



    //translate all the tabs to spaces

    char *tmp = keys;

    while( strchr(tmp,'\t') ) {

        tmp = strchr(tmp,'\t');

        [206]*tmp++ = ' ';

    }

At line 178, the number of chars per pixel is provided and read into a signed integer. This value is then used without further checks at line 200 to find the start of the color keys area. This memory location is then written to by replacing tabs with spaces.

Mitigation

One way to mitigate this is to explicitly check wether a file is detected as being an XPM file by checking the return value of FreeImageGetFileType() or FreeImageGetFIFFromFilename().

If the return value of either of these functions is FIF_XPM, then the subsequent loading function should not be called.

Timeline

2016-07-29 - Vendor Disclosure
2016-10-03 - Public Release

Credit

Discovered by Yves Younan of Cisco Talos.