Information

File: PtpMgr/PtpApp/PtpOperation.c
Function: PTP_SendObjectInfo()

The handler uses the (unchecked) filename length field (1 byte) to copy what was supposed to be a unicode string, into a buffer of size 0x40. Being a unicode string, the sent length field is multiplied by two, giving us a memcpy in the range: [0:0x1FE], way more than the destination buffer of 0x40.
The destination buffer is at offset 0x1A4 in the global context, and at offset 0x1E8 there is a pointer that will be freed right after our copy, granting us an absolute “Free-Where” primitive.

Decompiling the vulnerable code will look roughly like this:

input_size = ctx->get_data_size_ptr(ctx->handle);
if ( !input_size )
    return 0;
pMessage = PTP_AllocateMemory(input_size);
if ( !pMessage )
{
    ... // error handling
    return 1;
}
if ( ctx->recv_data_ptr(ctx->handle, pMessage, input_size) < 0 )
{
    ... // error handling
    return 0;
}
ObjectFormat = pMessage[4] | (pMessage[5] << 8);
if ( ObjectFormat != Script && ObjectFormat != UNDOCUMENTED_writable )
{
    goto label_error_resp;
}
pMemoryContext = global_ptp_memory_context;
global_ptp_memory_context->some_handle = some_handle;
// EI-DBG: Attacker controllen length field - 1 Byte
filename_len_1_byte = pMessage[52];
// EI-DBG: Memcpy using attacker controlled length field (* 2)
memcpy(pMemoryContext->unicode_filename, pMessage + 53, 2 * filename_len_1_byte);
global_ptp_memory_context->object_input_size = 0;
pMemoryContext->filename_len = filename_len_1_byte;
keywords_len_1_byte = pMessage[2 * filename_len_1_byte + 55];
pMemoryContext->keywords_len = keywords_len_1_byte;
// EI-DBG: Going to free() "pKeywordsStringUnicode" that was replaced by the attacker
if ( pMemoryContext->pKeywordsStringUnicode)
{
    PTP_FreeMemory(pMemoryContext->pKeywordsStringUnicode);
    global_ptp_memory_context->pKeywordsStringUnicode = 0;
}


References:
https://research.checkpoint.com/say-cheese-ransomware-ing-a-dslr-camera
https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-5994