TALOS-2016-0047
FFMpeg LibAvCodec SMC Opcodes 6,7 Heap-based Buffer overflow
April 7, 2016
Report ID
CVE-2016-1528
Description
This vulnerability is located within the libavcodec library within the ffmpeg project. The libavcodec library is responsible for parsing codec streams within ffmpeg and hence is used in various open-source projects. Within the SMC decoder, which is responsible for decoding QuickTime Video sample data, is a heap-based buffer overflow and was introduced during revision 2488 to the project. The overflow occurs when decoding video sample data and can be used to write outside the video frame. This can be used to overwrite heap data which can lead to code execution under the context of the application.
Product Urls
http://www.ffmpeg.org
https://github.com/FFmpeg/FFmpeg
Details
The SMC codec’s sample data can be described as a stream of opcodes. Each element is a 3-bit opcode followed by a single-bit that is used to describe whether a count is encoded in the next 4-bits or within an octet that follows the opcode. Each of these elements act on the video frame by applying an operation to a 4x4 block.
When parsing a container that utilizes this codec, the decoder will chunk the frame into 4x4 blocks and then proceed to apply each opcode to the video frame. When the decoder encounters opcode 3, an 8-bit pixel color will be pulled from the sample stream. This pixel will then be used to fill the specified number of blocks with. The pixel color comes the palette defined within the movie container.
libavformat/smc.c:80
static void smc_decode_stream(SmcContext *s)
{
...
unsigned char *pixels = s->frame->data[0];
...
int row_ptr = 0;
int pixel_ptr = 0;
...
total_blocks = ((s->avctx->width + 3) / 4) * ((s->avctx->height + 3) / 4);
...
while (total_blocks) {
...
opcode = bytestream2_get_byte(&s->gb);
switch (opcode & 0xF0) {
...
/* 1-color block encoding */
case 0x60: // 3
case 0x70:
n_blocks = GET_BLOCK_COUNT(); // XXX: get the count
pixel = bytestream2_get_byte(&s->gb); // XXX: get the color to fill the block with
while (n_blocks--) {
block_ptr = row_ptr + pixel_ptr;
for (pixel_y = 0; pixel_y < 4; pixel_y++) {
for (pixel_x = 0; pixel_x < 4; pixel_x++) {
pixels[block_ptr++] = pixel; // XXX: fill the 4x4 block with the specified color
}
block_ptr += row_inc;
}
ADVANCE_BLOCK();
}
break;
...
}
}
Due to a lack of bounds checking when decoding this opcode, an aggressor can specify a block count that will cause the inner loop to write a number of 4x4 blocks of the specified color outside the bounds of the video frame. Due to this, a heap-based buffer overflow can be made to occur.
Crash Analysis
The provided proof-of-concept was developed against ffplay.exe which utilizes libavcodec for decoding. ffplay.exe was built with MinGW under the Msys environment and run with Full-page heap utilizing gflags.exe +hpa.
(239c.2010): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=0000007f ebx=00000000 ecx=00000004 edx=00000440 esi=0000001c edi=074f7bc0
eip=007ea931 esp=0ab6fac0 ebp=00000400 iopl=0 nv up ei pl nz ac po nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010212
image00400000+0x3ea931:
007ea931 880417 mov byte ptr [edi+edx],al ds:002b:074f8000=??
0:003> !heap -p -a @edi
address 074f7bc0 found in
_DPH_HEAP_ROOT @ 74d1000
in busy allocation ( DPH_HEAP_BLOCK: UserAddr UserSize - VirtAddr VirtSize)
74d1bf8: 74f7bb8 442 - 74f7000 2000
629e8e89 verifier!AVrfDebugPageHeapAllocate+0x00000229
775d104e ntdll!RtlDebugAllocateHeap+0x00000030
7758b16e ntdll!RtlpAllocateHeap+0x000000c4
77533074 ntdll!RtlAllocateHeap+0x0000023a
76ea9d45 msvcrt!malloc+0x0000008d
76ed02f4 msvcrt!_aligned_offset_malloc+0x0000008a
76ed029f msvcrt!_aligned_malloc_dbg+0x00000012
Description of the proof-of-concept
Within ffmpeg, the SMC codec is only available via the ISO standardized .MP4 format. This format as well as all movie containers are implemented by libavformat is and is the same as utilized by Apple Quicktime’s MOV format. Within the MP4 format, is an atom known as the Sample Description (STSD) atom. The Sample description atom contains a fourcc ‘SMC ‘ in the data format field and then contains an array of format-specific data. Within the provided poc, the Sample Description atom is located at offset 0x1fec.
<class atom.Atom> '0'
[1fec] <instance be(pQTInt) 'size'> 0x0000086e (2158)
[1ff0] <instance be(pQTType) 'type'> type 'stsd' (73747364)
[1ff4] <instance be(uint_t) 'extended_size'> 0x0 (0)
[1ff4] <instance c(atom.stsd) 'data'> "\x00\x00\x00\x00\x00\x00\x00 ..skipped ~2130 bytes.. \x00\x00\xff\xff\xff\xff"
The first entry in the array selects the video stream used by the container. In order to select the SMC codec, the Data format field must be ‘smc ‘. This is at offset 0x2000 in the provided poc.
<class atom.Entry> '0'
[1ffc] <instance be(pQTInt) 'Sample description size'> 0x0000085e (2142)
[2000] <instance be(pQTType) 'Data format'> Data format 'smc ' (736d6320)
[2004] <instance dynamic.block(6) 'Reserved'> "\x00\x00\x00\x00\x00\x00"
[200a] <instance be(uint16_t) 'Data reference index'> 0x0001 (1)
[200c] <instance dynamic.block(2126) 'Data specific'> "\x00\x01\x00\x02\x61\x70\x70 ..skipped ~2106 bytes.. \x00\x00\xff\xff\xff\xff"
At the very end of the entry, contains codec-specific data. If the codec format decribes a video sample, the following structure can be located. Within this structure is the frame’s width and height. Whereas the width and height can also be described in the TKHD atom, the width and height defined within the STSD atom is given priority and used to allocate a pool of video frames by ffmpeg.
<class atom.Video> '0x41787d8'
[200c] <instance be(uint16_t) 'Version'> 0x0001 (1) // XXX: ignored by ffmpeg
[200e] <instance be(uint16_t) 'Revision level'> 0x0002 (2)
[2010] <instance be(pQTType) 'Vendor'> Vendor 'appl' (6170706c)
[2014] <instance be(pQTInt) 'Temporal Quality'> 0x00000000 (0)
[2018] <instance be(pQTInt) 'Spatial Quality'> 0x00000400 (1024)
[201c] <instance be(uint16_t) 'Width'> 0x0258 (600) // XXX: used to control allocation
[201e] <instance be(uint16_t) 'Height'> 0x0190 (400) // XXX: of frame within the frame pool
[2020] <instance be(pQTInt) 'Horizontal Resolution'> 0x00480000 (4718592)
[2024] <instance be(pQTInt) 'Vertical Resolution'> 0x00480000 (4718592)
[2028] <instance atom.MediaVideo_v1 'Versioned'> "\x00\x00\x00\x00\x00\x01\x08 ..skipped ~2078 bytes.. \x00\x00\xff\xff\xff\xff"
At offset 0x2028 within the PoC is the bit-depth. The SMC codec utilizes a palette in order to fill each 4x4 block. If the bit-depth has it’s 6th bit set (0x20), the palette will be grayscale. If the bit-depth is 1,2,4,or 8 a default palette wlil be set based on the Color Table Id. If Color Table Id is defined as 0, a non-default palette can be provided. This can allow the agressor to control the exact values that can be written by an opcode.
<class atom.MediaVideo_v1> 'Versioned'
[2028] <instance be(primitives.pQTInt) 'Data size'> 0x00000000 (0)
[202c] <instance be(pint.uint16_t) 'Frame Count'> 0x0001 (1)
[202e] <instance atom.CompressorName 'Compressor Name'> "\x08\x47\x72\x61\x70\x68\x69\x63\x73\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
[204f] <instance pint.uint16_t 'Depth'> 0x0008 (8) // XXX: Must be one of the values defined
[2051] <instance be(pint.uint16_t) 'Color Table ID'> 0x0000 (0) // XXX: To include a custom palette, specify 0
[2053] <instance atom.ColorTable 'Color Table'> "\x00\x00\x00\x00\x00\x00\xff ..skipped ~2035 bytes.. \x00\x00\xff\xff\xff\xff"
The color table or palette that is used to write with is located at offset 0x2053 within the file. This palette is used by the SMC decoder to populate each 4x4 block.
<class atom.ColorTable> 'Color Table'
[2053] <instance be(pint.uint32_t) 'start'> 0x00000000 (0)
[2057] <instance be(pint.uint8_t) 'count'> 0x00 (0)
[2058] <instance be(pint.uint16_t) 'end'> 0x00ff (255)
[205a] <instance dynamic.array(atom.argb,256) 'entries'> atom.argb[256] "\x00\x00\xff\xff\xff\xff\xff ..skipped ~2028 bytes.. \x00\x00\xff\xff\xff\xff"
Credit
Discovered by Cisco Talos
Timeline
2015-09-14: Initial Contact
2015-10-19: Second Vendor Contact
2015-11-14: Final attempt
2016-01-13: Reported to CERT
2016-04-08: Public Release