Ffmpeg-3.2 buffer overflow


Overview

A Heap Buffer Overflow vulnerability in FFmpeg-3.2 was found with AFL (http://lcamtuf.coredump.cx/afl/). The vulnerability was trigged when FFmpeg trying to decode an input image (a frame) to a JP2 file. The vulnerability is a Heap Buffer Overflow vulnerability due to some improper out-of-bound access check (in fact, an improper integer check). The vulnerability can cause Denial-of-Service and Information Disclosure and may cause more critical impact.

Environments

Operating System

Ubuntu 16.04 i686 Desktop
uname –a
Linux ubuntu 4.4.0-21-generic #37-Ubuntu SMP Mon Apr 18 18:34:49 UTC 2016 i686 i686 i686 GNU/Linux

Compilers

GCC + CLANG + rr

gcc --version  
gcc (Ubuntu 5.3.1-14ubuntu2.1) 5.3.1 20160413
              
clang --version
clang version 3.8.0-2ubuntu3 (tags/RELEASE_380/final)  
 
rr --version 
rr version 4.4.0

Reproduction

The crash can be trigged by executing ‘ffmpeg’ (or its debug version, ‘ffmpeg_g’) with the PoC image file as its input and output to a JP2 file.

GCC version:

mkdir build-gcc-debug<br/>
cd build-gcc-debug
../configure --extrac-cflags=”-g” --extra-cxxflags=”-g” --extra-ldflags=”-g” --enable-debug  
make
./ffmpeg –i  /* the poc file */ a.jp2` 

Clang with asan:

mkdir build-clang-asan
cd build-clang-asan
../configure --cc=clang --cxx=clang++ --extra-cflags=”-O1 -fno-omit-frame-pointer -g--extra-cxxflags=”-O1 -fno-omit-frame-pointer -g--extra-ldflags=”-fsanitize=address” --enable-debug
make
./ffmpeg –i /* the poc file */ a.jp2

EXCEPTION

ffmpeg version N-82145-g0779396 Copyright (c) 2000-2016 the FFmpeg developers
  built with clang version 3.8.0-2ubuntu4 (tags/RELEASE_380/final)
  configuration: --cc=clang --cxx=clang++ --extra-cflags='-O1 -fno-omit-frame-pointer -g' --extra-cxxflags='-O1 -fno-omit-frame-pointer -g' --extra-ldflags='-fsanitize=address' --enable-debug
  libavutil      55. 35.100 / 55. 35.100
  libavcodec     57. 65.100 / 57. 65.100
  libavformat    57. 57.100 / 57. 57.100
  libavdevice    57.  2.100 / 57.  2.100
  libavfilter     6. 66.100 /  6. 66.100
  libswscale      4.  3.100 /  4.  3.100
  libswresample   2.  4.100 /  2.  4.100
=============================================================
==18178==ERROR: AddressSanitizer: heap-buffer-overflow on address 0xb4202ff8 at pc 0x080f0a58 bp 0xbff3b998 sp 0xbff3b570
READ of size 3 at 0xb4202ff8 thread T0
    #0 0x80f0a57  (/home/fire/bing/afl/libraries/ffmpeg/build-clang-asan/ffmpeg+0x80f0a57)
    #1 0x8783a0c  (/home/fire/bing/afl/libraries/ffmpeg/build-clang-asan/ffmpeg+0x8783a0c)
 
0xb4202ff8 is located 1 bytes to the right of 119-byte region [0xb4202f80,0xb4202ff7)
allocated by thread T0 here:
    #0 0x813d734  (/home/fire/bing/afl/libraries/ffmpeg/build-clang-asan/ffmpeg+0x813d734)
    #1 0x8d787ff  (/home/fire/bing/afl/libraries/ffmpeg/build-clang-asan/ffmpeg+0x8d787ff)
    #2 0xbff3bb7f  (<unknown module>)
 
SUMMARY: AddressSanitizer: heap-buffer-overflow (/home/fire/bing/afl/libraries/ffmpeg/build-clang-asan/ffmpeg+0x80f0a57)
Shadow bytes around the buggy address:
  0x368405a0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x368405b0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x368405c0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x368405d0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x368405e0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
=>0x368405f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 07[fa]
  0x36840600: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x36840610: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x36840620: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x36840630: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x36840640: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07
  Heap left redzone:       fa
  Heap right redzone:      fb
  Freed heap region:       fd
  Stack left redzone:       f1
  Stack mid redzone:       f2
  Stack right redzone:      f3
  Stack partial redzone:    f4
  Stack after return:       f5
  Stack use after scope:    f8
  Global redzone:         f9
  Global init order:        f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:     bb
  ASan internal:           fe
  Left alloca redzone:      ca
  Right alloca redzone:     cb
==18178==ABORTING

Analysis

The crash happens in function “sunrast_decode_frame” when copying data from source buffer (aka. avpkt->data) to destination buffer (aka. ptr).

crash stack

Program received signal SIGSEGV, Segmentation fault.
__memcpy_sse2_unaligned () at ../sysdeps/i386/i686/multiarch/memcpy-sse2-unaligned.S:651
651  ../sysdeps/i386/i686/multiarch/memcpy-sse2-unaligned.S: No such file or directory.
(rr) bt
#0  __memcpy_sse2_unaligned () at ../sysdeps/i386/i686/multiarch/memcpy-sse2-unaligned.S:651
#1  0x08682438 in memcpy (__len=3, __src=0xb4a1000, __dest=<optimized out>) at /usr/include/i386-linux-gnu/bits/string3.h:53
#2  sunrast_decode_frame (avctx=0xb481760, data=0xb481e80, got_frame=0xbff30568, avpkt=0xbff304d8) at src/libavcodec/sunrast.c:173
#3  0x086bb643 in avcodec_decode_video2 (avctx=0xb481760, picture=0xb481e80, got_picture_ptr=0xbff30568, avpkt=0xbff30668) at src/libavcodec/utils.c:2257
#4  0x086bc5fd in do_decode (avctx=avctx@entry=0xb481760, pkt=pkt@entry=0xbff30668) at src/libavcodec/utils.c:2788
#5  0x086bd3bb in avcodec_send_packet (avctx=0xb481760, avpkt=<optimized out>) at src/libavcodec/utils.c:2877
#6  0x0831d4be in try_decode_frame (s=s@entry=0xb4801e0, st=st@entry=0xb480fe0, avpkt=avpkt@entry=0xbff307f0, options=0xb480aa0) at src/libavformat/utils.c:2967
#7  0x08327962 in avformat_find_stream_info (ic=0xb4801e0, options=0xb480aa0) at src/libavformat/utils.c:3668
#8  0x080caa97 in open_input_file (o=o@entry=0xbff30b0c, filename=<optimized out>) at src/ffmpeg_opt.c:1019
#9  0x080cd657 in open_files (l=0xb48002c, l=0xb48002c, open_file=0x80ca600 <open_input_file>, inout=0x8c6d542 "input")
at src/ffmpeg_opt.c:3135
#10 ffmpeg_parse_options (argc=4, argv=0xbff312a4) at src/ffmpeg_opt.c:3175
#11 0x080bca58 in main (argc=4, argv=0xbff312a4) at src/ffmpeg.c:4564

The code around the crash point:

src/libavcodec/sunrast.c
170            for (y = 0; y < h; y++) {
171                if (buf_end - buf < len)
172                    break;
173                memcpy(ptr, buf, len);
174                ptr += stride;
175                buf += alen;
176     }

The crash happens when copying data from ‘buf’ to ‘ptr’ which can cause out-of-bound read/write. The above code has already included a buffer-overflow check, aka line 171-172, however, the check is incomplete and can be bypassed when ‘alen’ is greater than ‘len’ and (buf_end - buf) % alen (aka. remainder) is not less than ‘len’ (in fact, equal to ‘len’). When ‘alen’ and ‘len’ satisfy the noted condition, ‘buf’ will pass through ‘buf_end’ (i.e. ‘buf’ becomes greater than ‘buf_end’) and (buf_end - buf) will become a negative number, here, (buf_end - buf) will become a big unsigned positive integer because type of ‘buf_end’ and ‘buf’ is “unsigned char*” i.e. unsigned int; then, the check (line 171-172) will become useless. Further, out-of-bound read\write will happen when (len * h) is greater than the length of ‘buf’.

Next, I need to know which related variables (such as h, buf_end, len, alen, buf, ptr, stride) can be controlled by attackers.

=================The related code in src/libavcodec/sunrast.c======
29     static int sunrast_decode_frame(AVCodecContext *avctx, void *data,
30                                  int *got_frame, AVPacket *avpkt)
31     {
32              const uint8_t *buf       = avpkt->data;
33              const uint8_t *buf_end   = avpkt->data + avpkt->size;

48              w         = AV_RB32(buf + 4);
49              h         = AV_RB32(buf + 8);
50              depth     = AV_RB32(buf + 12);
51              type      = AV_RB32(buf + 20);
52              maptype   = AV_RB32(buf + 24);
53              maplength = AV_RB32(buf + 28);
54              buf      += 32;

127            buf += maplength;

140            len  = (depth * w + 7) >> 3;
141            alen = len + (len & 1);
==========================================================

From the above related code, I know those variables almost come from “avpkt->data”. Checking the content of “avpkt->data”, I found the content is same with the content of the input poc file, i.e. “avpkt->data” stores the content read in from the poc file.

=============================DEBUG WITH RR===============
(rr) break sunrast_decode_frame
Breakpoint 1 at 0x86820f0: file src/libavcodec/sunrast.c, line 31.
(rr) reverse-continue
Continuing.
 
Program received signal SIGSEGV, Segmentation fault.
__memcpy_sse2_unaligned ()
    at ../sysdeps/i386/i686/multiarch/memcpy-sse2-unaligned.S:651
651  in ../sysdeps/i386/i686/multiarch/memcpy-sse2-unaligned.S
(rr) reverse-continue
Continuing.
 
Breakpoint 1, sunrast_decode_frame (avctx=0xb481760, data=0xb481e80, got_frame=0xbff30568, avpkt=0xbff304d8) at src/libavcodec/sunrast.c:31
31     {
(rr) p/x avpkt
$22 = 0xbff304d8
(rr) p/x *avpkt
$23 = {buf = 0xb482280, pts = 0x7ffeffffffffffff, dts = 0x7ffeffffffffffff, data = 0xb4821d8, size = 0x57, stream_index = 0x0, flags = 0x1, side_data = 0x0, side_data_elems = 0x0, duration = 0x1, pos = 0x0, convergence_duration = 0x0}
(rr) p/x avpkt->size
$24 = 0x57
(rr) x/87b avpkt->data
0xb4821d8:      0x59 0xa6 0x6a 0x95 0x00 0x00 0x00 0x01
0xb4821e0:      0x00 0x00 0xde 0x01 0x00 0x00 0x00 0x18
0xb4821e8:      0x00 0x20 0x00 0x01 0x00 0x00 0x00 0x01
0xb4821f0:      0x00 0x00 0x00 0x01 0x00 0x00 0x00 0x00
0xb4821f8:      0x01 0x00 0x18 0x00 0x00 0x00 0x00 0x00
0xb482200:      0x00 0x00 0x00 0x00 0x00 0x18 0x00 0x20
0xb482208:      0x00 0x01 0x00 0x00 0x00 0x01 0x00 0x00
0xb482210:      0x00 0x01 0x00 0x00 0x00 0x00 0x01 0x00
0xb482218:      0x18 0x00 0x00 0x6a 0x50 0x20 0x20 0x00
0xb482220:      0x00 0xff 0x00 0x00 0x00 0x00 0x00 0x00
0xb482228:      0xec 0x00 0x00 0x00 0x00 0x00 0x00
=============================================================

From the above code in function “sunrast_decode_frame”, I can infer the format of the input file, like the following:

---------------------------------------------------------------------------------------------
|  magic (4 bytes)  |   w (4bytes)    |    h (4 bytes)      |  depth (4 bytes)   |
---------------------------------------------------------------------------------------------
|   (4 bytes)      |   type (4 bytes)  |  maptype (4 bytes)  | maplength (4 bytes) |
---------------------------------------------------------------------------------------------
|                               data ((avpkt->size - 32) bytes)                      
---------------------------------------------------------------------------------------------

So, in this debugging (big-endian), magic = 0x5aa66a95, w = 0x01, h = 0xde01, depth = 0x18, type = 0x01, maptype = 0x01, maplength = 0x00, buf_end – buf_begin = 0x57
==> len = (0x18 * 0x01 + 7) » 3 = 0x03, alen = 0x03 + (0x03 & 0x01) = 0x04 ==> (buf_end – buf_begin - 0x20) / alen = 0x0d, (buf_end – buf_begin – 0x20) % alen = 0x03
==> when y = 0x0d, (buf_end – buf) = 0x03 (not less than ‘len’); when y = 14, buf_end – buf = -1 = 0xffffffff > len

Beside the above analysis, in order to confer that buffer overflow is possible, I need to know the max loop times and the capacity of buffer ‘buf’ (aka. avpkt->data). The max loop times is h which can be directly controlled from the input file (here is 0xde01) and the max number of bytes read from ‘buf’ is (h * len). Next, I need to know the capacity of buffer ‘avpkt->data’.

========================DEBUG WITH RR=======================
(rr) p avpkt
$1 = (AVPacket *) 0xbff304d8
(rr) p/x *avpkt
$2 = {buf = 0xb482280, pts = 0x7ffeffffffffffff, dts = 0x7ffeffffffffffff, data = 0xb4821d8, size = 0x57, stream_index = 0x0, flags = 0x1, side_data = 0x0, side_data_elems = 0x0, duration = 0x1, pos = 0x0, convergence_duration = 0x0}
(rr) watch -l avpkt->data
Hardware watchpoint 2: -location avpkt->data
(rr) reverse-continue
Continuing.
 
Hardware watchpoint 2: -location avpkt->data
 
Old value = (uint8_t *) 0xb4821d8 "Y\246j\225"
New value = (uint8_t *) 0x8bb2251 <av_opt_find2+113> "\203\304\020\205\300\177\f\200"
avcodec_decode_video2 (avctx=0xb481760, picture=0xb481e80, got_picture_ptr=0xbff30568, avpkt=0xbff30668) at src/libavcodec/utils.c:2220
2220             AVPacket tmp = *avpkt;
(rr) p avpkt
$3 = (const AVPacket *) 0xbff30668
(rr) p &tmp
$4 = (AVPacket *) 0xbff304d8
(rr) p/x *avpkt
$5 = {buf = 0xb482280, pts = 0x7ffeffffffffffff, dts = 0x7ffeffffffffffff, data = 0xb4821d8, size = 0x57, stream_index = 0x0, flags = 0x1, side_data = 0x0, side_data_elems = 0x0, duration = 0x1, pos = 0x0, convergence_duration = 0x0}
(rr) watch -l avpkt->data
Hardware watchpoint 3: -location avpkt->data
(rr) disable 2
(rr) reverse-continue
Continuing.
 
Hardware watchpoint 3: -location avpkt->data
 
Old value = (uint8_t *) 0xb4821d8 "Y\246j\225"
New value = (uint8_t *) 0x20 <error: Cannot access memory at address 0x20>
try_decode_frame (s=s@entry=0xb4801e0, st=st@entry=0xb480fe0, avpkt=0xbff30804, avpkt@entry=0xbff307f0, options=0xb480aa0) at src/libavformat/utils.c:2912
2912             AVPacket pkt = *avpkt;
(rr) p avpkt
$6 = (AVPacket *) 0xbff30804
(rr) p &pkt
$7 = (AVPacket *) 0xbff30668
(rr) p/x *avpkt
$8 = {buf = 0xb4821d8, pts = 0x57, dts = 0x1, data = 0x0, size = 0x1, stream_index = 0x0, flags = 0x0, side_data = 0x0, side_data_elems = 0x0, duration = 0x0, pos = 0x0, convergence_duration = 0xb4808a00b480fe0}
(rr) break src/libavformat/utils.c:2912
Breakpoint 4 at 0x831d239: file src/libavformat/utils.c, line 2912.
(rr) reverse-continue
Continuing.
 
Breakpoint 4, try_decode_frame (s=s@entry=0xb4801e0, st=st@entry=0xb480fe0, avpkt=avpkt@entry=0xbff307f0, options=0xb480aa0) at src/libavformat/utils.c:2912
2912             AVPacket pkt = *avpkt;
(rr) p &pkt
$9 = (AVPacket *) 0xbff30668
(rr) p avpkt
$10 = (AVPacket *) 0xbff307f0
(rr) p/x *avpkt
$11 = {buf = 0xb482280, pts = 0x7ffeffffffffffff, dts = 0x7ffeffffffffffff, data = 0xb4821d8, size = 0x57, stream_index = 0x0, flags = 0x1, side_data = 0x0, side_data_elems = 0x0, duration = 0x1, pos = 0x0, convergence_duration = 0x0}
(rr) disable 3
(rr) watch -l avpkt->data
Hardware watchpoint 5: -location avpkt->data
(rr) disable 4
(rr) reverse-continue
Continuing.
 
Hardware watchpoint 5: -location avpkt->data
 
Old value = (uint8_t *) 0xb4821d8 "Y\246j\225"
New value = (uint8_t *) 0x3ff00000 <error: Cannot access memory at address 0x3ff00000>
0x08321e91 in read_frame_internal (s=s@entry=0xb4801e0, pkt=pkt@entry=0xbff307f0) at src/libavformat/utils.c:1570
1570                     *pkt = cur_pkt;
(rr) p pkt
$12 = (AVPacket *) 0xbff307f0
(rr) p &cur_pkt
$13 = (AVPacket *) 0xbff305b8
(rr) p/x cur_pkt
$14 = {buf = 0xb482280, pts = 0x8000000000000000, dts = 0x8000000000000000, data = 0xb4821d8, size = 0x57, stream_index = 0x0, flags = 0x1, side_data = 0x0, side_data_elems = 0x0, duration = 0x0, pos = 0x0, convergence_duration = 0x0}
(rr) disable 5
(rr) watch -l cur_pkt.data
Hardware watchpoint 6: -location cur_pkt.data
(rr) reverse-continue
Continuing.
 
Hardware watchpoint 6: -location cur_pkt.data
 
Old value = (uint8_t *) 0xb4821d8 "Hkg\267Hkg\267\320!H\v\320!H\v"
New value = (uint8_t *) 0x0
av_new_packet (pkt=0xbff305b8, size=87) at src/libavcodec/avpacket.c:95
95         pkt->data     = buf->data;
(rr) p pkt
$15 = (AVPacket *) 0xbff305b8
(rr) p buf
$16 = (AVBufferRef *) 0xb482280
(rr) disable 6
(rr) watch -l buf->data
Hardware watchpoint 7: -location buf->data
(rr) reverse-continue
Continuing.
 
Hardware watchpoint 7: -location buf->data
 
Old value = (uint8_t *) 0xb4821d8 "Hkg\267Hkg\267\320!H\v\320!H\v"
New value = (uint8_t *) 0x0
av_buffer_create (flags=0, opaque=0x0, free=0x8b93b70 <av_buffer_default_free>, size=119, data=0xb4821d8 "Hkg\267Hkg\267\320!H\v\320!H\v") at src/libavutil/buffer.c:55
55         ref->data   = data;
(rr) p ref
$17 = (AVBufferRef *) 0xb482280
=============================================================

From above debugging infomation, ‘avpkt->data’ comes from ‘data’ in function ‘av_buffer_create’. Next, the question becomes what size of ‘data’ is.

==========================DEBUG WITH RR=====================
(rr) bt
#0  av_buffer_create (flags=0, opaque=0x0, free=0x8b93b70 <av_buffer_default_free>, size=119, data=0xb4821d8 "Hkg\267Hkg\267\320!H\v\320!H\v") at src/libavutil/buffer.c:55
#1  av_buffer_realloc (pbuf=0xbff2f768, size=119) at src/libavutil/buffer.c:180
#2  0x0837c2ae in packet_alloc (size=87, buf=0xbff2f768) at src/libavcodec/avpacket.c:77
#3  av_new_packet (pkt=0xbff305b8, size=87) at src/libavcodec/avpacket.c:89
#4  0x0824b2ae in ff_img_read_packet (s1=0xb4801e0, pkt=0xbff305b8) at src/libavformat/img2dec.c:459
#5  0x0831df46 in ff_read_packet (s=0xb4801e0, pkt=0xbff305b8) at src/libavformat/utils.c:795
#6  0x08321b6c in read_frame_internal (s=s@entry=0xb4801e0, pkt=pkt@entry=0xbff307f0) at src/libavformat/utils.c:1493
#7  0x0832718c in avformat_find_stream_info (ic=0xb4801e0, options=0xb480aa0) at src/libavformat/utils.c:3537
#8  0x080caa97 in open_input_file (o=o@entry=0xbff30b0c, filename=<optimized out>) at src/ffmpeg_opt.c:1019
#9  0x080cd657 in open_files (l=0xb48002c, l=0xb48002c, open_file=0x80ca600 <open_input_file>, inout=0x8c6d542 "input") at src/ffmpeg_opt.c:3135
#10 ffmpeg_parse_options (argc=4, argv=0xbff312a4) at src/ffmpeg_opt.c:3175
#11 0x080bca58 in main (argc=4, argv=0xbff312a4) at src/ffmpeg.c:4564
============================================================= 

‘data’ is passed in by function ‘av_buffer_realloc’, and the related code is:

=====================function av_buffer_realloc===================
176            uint8_t *data = av_realloc(NULL, size);
177            if (!data)
178            return AVERROR(ENOMEM);
179          
180            buf = av_buffer_create(data, size, av_buffer_default_free, NULL, 0);
==============================================================

So, the size of data is ‘size’, which is passed in from function ‘packet_alloc’, and the related code is

====================function packet_alloc=======================
     77     ret = av_buffer_realloc(buf, size + AV_INPUT_BUFFER_PADDING_SIZE);
=============================================================

‘size’ is (size + AV_INPUT_BUFFER_PADDING_SIZE) and AV_INPUT_BUFFER_PADDING_SIZE is 32. Next, the question is what value of the current ‘size’ is. The current ‘size’ from function ‘ff_img_read_packet’ through function “av_new_packet”. The related code is

====================function ff_img_read_packet=================
     459            res = av_new_packet(pkt, size[0] + size[1] + size[2]);
=============================================================

Debugging:

============================DEBUG WITH RR=======================
(rr) break src/libavformat/img2dec.c:459
Breakpoint 8 at 0x824b2a0: file src/libavformat/img2dec.c, line 459.
(rr) reverse-continue
Continuing.
 
Breakpoint 8, ff_img_read_packet (s1=0xb4801e0, pkt=0xbff305b8) at src/libavformat/img2dec.c:459
459      res = av_new_packet(pkt, size[0] + size[1] + size[2]);
(rr) p/x size
$18 = {0x57, 0x0, 0x0}
(rr) watch -l size[0]
Hardware watchpoint 9: -location size[0]
(rr) reverse-continue
Continuing.
 
Hardware watchpoint 9: -location size[0]
 
Old value = 87
New value = 0
0x0824b967 in ff_img_read_packet (s1=0xb4801e0, pkt=0xbff305b8) at src/libavformat/img2dec.c:453
453              size[0] = avio_size(s1->pb);
(rr) break src/libavformat/img2dec.c:453
Breakpoint 10 at 0x824b95c: file src/libavformat/img2dec.c, line 453.
(rr) reverse-continue
Continuing.
 
Breakpoint 10, ff_img_read_packet (s1=0xb4801e0, pkt=0xbff305b8) at src/libavformat/img2dec.c:453
453              size[0] = avio_size(s1->pb);
(rr) s
avio_size (s=0xb488b40) at src/libavformat/aviobuf.c:310
310  {
(rr) n
313      if (!s)
(rr) n
316      if (!s->seek)
(rr) n
318      size = s->seek(s->opaque, 0, AVSEEK_SIZE);
(rr) s
io_seek (opaque=0xb480a40, offset=0, whence=65536) at src/libavformat/aviobuf.c:858
858      return ffurl_seek(internal->h, offset, whence);
(rr) s
ffurl_seek (h=0xb480980, pos=0, whence=65536) at src/libavformat/avio.c:435
435  {
(rr) n
438      if (!h->prot->url_seek)
(rr) n
440      ret = h->prot->url_seek(h, pos, whence & ~AVSEEK_FORCE);
(rr) s
442  }
(rr) s
440      ret = h->prot->url_seek(h, pos, whence & ~AVSEEK_FORCE);
(rr) s
file_seek (h=0xb480980, pos=0, whence=65536) at src/libavformat/file.c:235
235  {
(rr) n
236      FileContext *c = h->priv_data;
(rr) n
235  {
(rr) n
239      if (whence == AVSEEK_SIZE) {
(rr) n
236      FileContext *c = h->priv_data;
(rr) n
239      if (whence == AVSEEK_SIZE) {
(rr) n
241          ret = fstat(c->fd, &st);
(rr) n
242          return ret < 0 ? AVERROR(errno) : (S_ISFIFO(st.st_mode) ? 0 : st.st_size);
(rr) p/x ret
$19 = 0x0
(rr) p/x st
$20 = {st_dev = 0x801, __pad1 = 0x0, __st_ino = 0xc454b, st_mode = 0x8180, st_nlink = 0x1, st_uid = 0x3e8, st_gid = 0x3e8, st_rdev = 0x0, __pad2 = 0x0, st_size = 0x57, st_blksize = 0x1000, st_blocks = 0x8, st_atime = 0x58176c8d, st_atimensec = 0x224eaac, st_mtime = 0x58157526, st_mtimensec = 0x34912fd4, st_ctime = 0x58157526, st_ctimensec = 0x34912fd4, st_ino = 0xc454b}
(rr) p/x st.st_size
$21 = 0x57
(rr)

Finallly, I know it is the physical size of the input file.

So, the capacity of buffer ‘avpkt->data’ is (physical size of input file + 32) (here is 0x57 + 0x20 = 0x77). Crafting the poc file to make (h * len) greater than 0x77 (in this debugging) will cause out-of-bound read.

Also, I can analyze the capacity of buf ‘ptr’, however, the analysis is a little more complicated. I think it’s possible to trigger out-of-bound write to buffer ‘ptr’, however, it needs more skill to set ‘w’, ‘h’, ‘depth’ and craft the sample to make it have proper physical size.

Conclusions

The vulnerability is a Heap Buffer Overflow vulnerability due to an incomplete buffer-overflow check which are already there and can be bypassed by crafting an input image with proper header data. The vulnerability can cause Denial-of-Service, Information Disclosure and may cause more critical impact.

This vulnerability was firstly reported to RedHat Security Team. Now it has been confirmed by the upstream and has been patched, which can be found by this link: http://ffmpeg.org/pipermail/ffmpeg-cvslog/2016-November/102693.html