|
Message-ID: <54BE7D7A.3010700@cs.uni-goettingen.de> Date: Tue, 20 Jan 2015 17:08:26 +0100 From: Fabian Yamaguchi <fabian.yamaguchi@...uni-goettingen.de> To: <oss-security@...ts.openwall.com> CC: konrad Rieck <konrad.rieck@...-goettingen.de> Subject: Vulnerabilities in VLC 2.1.5 Hi oss-security, (please note, I'm not on the list.) I recently discovered a couple of vulnerabilities in the latest stable version of VLC (2.1.5), reported them to the developers and also provided patches, most of which were applied. The most critical issues are a buffer-overflow in the mp4-demuxer and another in the automatic updater. For the last flaw, I also showed at 31C3 that it can indeed be leveraged for arbitrary code execution. Below you find links to the patches. Please note, that patches were applied for the master-branch, so they may not all be immediately applicable to 2.1.5. However, the attached original bug reports give you all the details for 2.1.5. * Buffer overflow in updater: https://github.com/videolan/vlc/commit/fbe2837bc80f155c001781041a54c58b5524fc14 * Buffer overflow in mp4 demuxer: https://github.com/videolan/vlc/commit/2e7c7091a61aa5d07e7997b393d821e91f593c39 * Potential buffer overflow in Schroedinger Encoder https://github.com/videolan/vlc/commit/9bb0353a5c63a7f8c6fc853faa3df4b4df1f5eb5 * Invalid memory access in rtp code: https://github.com/videolan/vlc/commit/204291467724867b79735c0ee3aeb0dbc2200f97 * Null-pointer dereference in dmo codec: https://github.com/videolan/vlc/commit/229c385a79d48e41687fae8b4dfeaeef9c8c3eb7 I was wondering whether anybody could assign CVEs for these vulnerabilities. Please note that the following problems were not fixed: * The potential buffer overflow in the Dirac Encoder was not fixed as the Dirac encoder no longer exists in the master branch. * The potential invalid writes in modules/services_discovery/sap.c and modules/access/ftp.c were not fixed as I did not provide a trigger. Note, that the code looks very similar to the confirmed bug in rtp_packetize_xiph_config, and so I leave it to you to decide whether you want to patch this. I have not attached the triggers mentioned in the report. If anybody is interested in these, please let me know. Kind Regards, Fabian Yamaguchi - University of Goettingen Original Bug Reports ===================== Buffer Overflow in MP4 Demuxer =============================== The function MP4_ReadBox_name in modules/demux/mp4/libmp4.c contains a buffer overflow. The following shows the vulnerable code: ---- snip ---- static int MP4_ReadBox_name( stream_t *p_stream, MP4_Box_t *p_box ) { MP4_READBOX_ENTER( MP4_Box_data_name_t ); p_box->data.p_name->psz_text = malloc( p_box->i_size + 1 - 8 ); //(1) if( p_box->data.p_name->psz_text == NULL ) MP4_READBOX_EXIT( 0 ); memcpy( p_box->data.p_name->psz_text, p_peek, p_box->i_size - 8 ); //(2) p_box->data.p_name->psz_text[p_box->i_size - 8] = '\0'; #ifdef MP4_VERBOSE msg_Dbg( p_stream, "read box: \"name\" text=`%s'", p_box->data.p_name->psz_text ); #endif MP4_READBOX_EXIT( 1 ); } ---- /snip ---- The attacker controls the size of the MP4 Box (p_box->i_size). If set to 7, the argument passed to malloc at (1) is 0, and hence, the buffer p_box->data.p_name->psz_text is close to 0 bytes long. The call to memcpy at //(2) then copies "(size_t) -1" bytes into the buffer, causing an overflow. We have attached the file crafted-mp4.mp4 that triggers this issue. To reproduce it, you can place the file in your working directory and run: vlc ./crafted-mp4.mp4. On our test systems, this produces the following stack-trace: ---- snip ---- Program received signal SIGSEGV, Segmentation fault. [Switching to Thread 0x7fffee04a700 (LWP 1349)] 0x00007ffff68858e2 in __memcpy_avx_unaligned () from /usr/lib/libc.so.6 (gdb) bt #0 0x00007ffff68858e2 in __memcpy_avx_unaligned () from /usr/lib/libc.so.6 #1 0x00007fffedb36fc2 in MP4_ReadBox_name (p_stream=0x7fffdcc04a08, p_box=0x7fffdcd2b840) at mp4/libmp4.c:2603 #2 0x00007fffedb39e9f in MP4_ReadBox (p_stream=0x7fffdcc04a08, p_father=0x7fffdcd2b5f0) at mp4/libmp4.c:3428 #3 0x00007fffedb28ba5 in MP4_ReadBoxContainerChildren (p_stream=0x7fffdcc04a08, p_container=0x7fffdcd2b5f0, i_last_child=0) at mp4/libmp4.c:210 #4 0x00007fffedb28c55 in MP4_ReadBoxContainerRaw (p_stream=0x7fffdcc04a08, p_container=0x7fffdcd2b5f0) at mp4/libmp4.c:230 #5 0x00007fffedb28cd5 in MP4_ReadBoxContainer (p_stream=0x7fffdcc04a08, p_container=0x7fffdcd2b5f0) at mp4/libmp4.c:246 #6 0x00007fffedb39e9f in MP4_ReadBox (p_stream=0x7fffdcc04a08, p_father=0x7fffdcd2b4e0) at mp4/libmp4.c:3428 #7 0x00007fffedb28ba5 in MP4_ReadBoxContainerChildren (p_stream=0x7fffdcc04a08, p_container=0x7fffdcd2b4e0, i_last_child=0) at mp4/libmp4.c:210 #8 0x00007fffedb28c55 in MP4_ReadBoxContainerRaw (p_stream=0x7fffdcc04a08, p_container=0x7fffdcd2b4e0) at mp4/libmp4.c:230 #9 0x00007fffedb37e61 in MP4_ReadBox_meta (p_stream=0x7fffdcc04a08, p_box=0x7fffdcd2b4e0) at mp4/libmp4.c:2818 #10 0x00007fffedb39e9f in MP4_ReadBox (p_stream=0x7fffdcc04a08, p_father=0x7fffdcd2b480) at mp4/libmp4.c:3428 #11 0x00007fffedb28ba5 in MP4_ReadBoxContainerChildren (p_stream=0x7fffdcc04a08, p_container=0x7fffdcd2b480, i_last_child=0) at mp4/libmp4.c:210 #12 0x00007fffedb28c55 in MP4_ReadBoxContainerRaw (p_stream=0x7fffdcc04a08, p_container=0x7fffdcd2b480) at mp4/libmp4.c:230 #13 0x00007fffedb28cd5 in MP4_ReadBoxContainer (p_stream=0x7fffdcc04a08, p_container=0x7fffdcd2b480) at mp4/libmp4.c:246 #14 0x00007fffedb39e9f in MP4_ReadBox (p_stream=0x7fffdcc04a08, p_father=0x7fffdcc05810) at mp4/libmp4.c:3428 #15 0x00007fffedb28ba5 in MP4_ReadBoxContainerChildren (p_stream=0x7fffdcc04a08, p_container=0x7fffdcc05810, i_last_child=0) at mp4/libmp4.c:210 #16 0x00007fffedb28c55 in MP4_ReadBoxContainerRaw (p_stream=0x7fffdcc04a08, p_container=0x7fffdcc05810) at mp4/libmp4.c:230 #17 0x00007fffedb28cd5 in MP4_ReadBoxContainer (p_stream=0x7fffdcc04a08, p_container=0x7fffdcc05810) at mp4/libmp4.c:246 ---Type <return> to continue, or q <return> to quit--- #18 0x00007fffedb39e9f in MP4_ReadBox (p_stream=0x7fffdcc04a08, p_father=0x7fffdcc056c0) at mp4/libmp4.c:3428 #19 0x00007fffedb28ba5 in MP4_ReadBoxContainerChildren (p_stream=0x7fffdcc04a08, p_container=0x7fffdcc056c0, i_last_child=1987014509) at mp4/libmp4.c:210 #20 0x00007fffedb3a345 in MP4_BoxGetRoot (s=0x7fffdcc04a08) at mp4/libmp4.c:3592 #21 0x00007fffedb1e8ef in LoadInitFrag (p_demux=0x7fffdcc04c38, b_smooth=false) at mp4/mp4.c:235 #22 0x00007fffedb1edaa in Open (p_this=0x7fffdcc04c38) at mp4/mp4.c:361 We believe that this flaw is exploitable for arbitrary code execution, albeit it may not be easy. Since VLC is a multi-threaded application, techniques similar to those used in the exploit for Apache mod-setenvif described in http://www.halfdog.net/Security/2011/ApacheModSetEnvIfIntegerOverflow/DemoExploit.html can possibly be leveraged to achieve this. ---- /snip ---- Buffer Overlow in the Updater ============================= I have one more buffer overflow to report in the updater affecting both the stable release and the master branch. A possible patch against the master branch is attached. The function `GetUpdateFile` in src/misc/update.c contains a buffer overflow due to an integer truncation. At (1) the length of the update-file downloaded from the update server is stored in a 64 bit integer. While adding '1' to this integer inside the argument to malloc at (2) at first succeeds, to pass the value to malloc, it is cast to size_t. On 32 bit platforms (or 64 bit platforms running the 32 bit version of VLC), size_t is 4 byte wide, and thus, a truncation occurs. If the file size is chosen to be 0xffffffff, then close to 0 bytes are allocated. The call to stream_Read at (3) then copies 0xffffffff bytes into this buffer, causing an overflow. static bool GetUpdateFile( update_t *p_update ) { stream_t *p_stream = NULL; char *psz_version_line = NULL; char *psz_update_data = NULL; p_stream = stream_UrlNew( p_update->p_libvlc, UPDATE_VLC_STATUS_URL ); if( !p_stream ) { msg_Err( p_update->p_libvlc, "Failed to open %s for reading", UPDATE_VLC_STATUS_URL ); goto error; } const int64_t i_read = stream_Size( p_stream ); // (1) psz_update_data = malloc( i_read + 1 ); // (2) if( !psz_update_data ) goto error; if( stream_Read( p_stream, psz_update_data, i_read ) != i_read ) //(3) { msg_Err( p_update->p_libvlc, "Couldn't download update file %s", UPDATE_VLC_STATUS_URL ); goto error; } psz_update_data[i_read] = '\0'; ... } We have tested whether this vulnerability can be triggered by redirecting all requests to update.videolan.org to a local web-server that hosts a file named vlc/status-win-x86 containing 0xffffffff 'A's, and then clicking on 'Help->Check for updates' in VLC. This bug should also be triggered when automatically checking for updates, so this flaw can probably be triggered without user interaction. Since VLC is a multithreaded application, different results were observed in the debugger. Most notably, in several runs, the process crashed with an instruction pointer of 0x41414141 ("AAAA"), which strongly suggests that this flaw is remotely exploitable for arbitrary code execution. Memory corruption in RTP Code ============================== The function rtp_packetize_xiph_config in modules/stream_out/rtpfmt.c allows an out-bof-bounds write-access to be caused when streaming a crafted Ogg Vorbis file RTP. The following excerpt of rtpfmt.c shows the vulnerable code: ---- snip ---- int rtp_packetize_xiph_config( sout_stream_id_t *id, const char *fmtp, int64_t i_pts ) { if (fmtp == NULL) return VLC_EGENERIC; /* extract base64 configuration from fmtp */ char *start = strstr(fmtp, "configuration="); assert(start != NULL); start += sizeof("configuration=") - 1; char *end = strchr(start, ';'); assert(end != NULL); size_t len = end - start; // (0) char b64[len + 1]; // (1) memcpy(b64, start, len); // (2) b64[len] = '\0'; ... } ---- /snip ---- rtp_packetize_xiph_config allocates a buffer on the stack at (1) where the size depends on the local variable 'len'. The variable 'len' is calculated at (0) to be the length of a string contained in the Ogg Vorbis file, and therefore, it is attacker-controlled. If the amount of stack memory is not sufficient to hold the buffer (a 'len' value of 1052896 byte was enough in our tests on a 64bit Linux machine with 8 GB RAM), the start of the buffer will point to a location outside of the stack. Subsequently copying data into the buffer at (2) will then corrupt non-stack memory. We have attached a test-case (input786432.ogg) that highlights this problem by causing an invalid memory access at (2). The crash can be triggered by placing the attached file in the working directory and running the following command: vlc input786432.ogg --sout '#rtp{dst=localhost,sdp=rtsp://localhost:8080/foo.sdp}' This leads to the following stack-trace: ---- snip ---- Program received signal SIGSEGV, Segmentation fault. 0x00007fffc6e87ab1 in memcpy (__len=1052896, __src=0x7fffc0595e3e, __dest=0x7fffc74a6cc0) at /usr/include/x86_64-linux-gnu/bits/string3.h:51 51 return __builtin___memcpy_chk (__dest, __src, __len, __bos0 (__dest)); (gdb) where #0 0x00007fffc6e87ab1 in memcpy (__len=1052896, __src=0x7fffc0595e3e, __dest=0x7fffc74a6cc0) at /usr/include/x86_64-linux-gnu/bits/string3.h:51 #1 rtp_packetize_xiph_config (id=id@...ry=0x7fffc0004140, fmtp=<optimized out>, i_pts=4619686105) at rtpfmt.c:563 #2 0x00007fffc6e81412 in Send (p_stream=<optimized out>, id=0x7fffc0004140, p_buffer=0x7fffdcde0650) at rtp.c:1283 #3 0x00007ffff796f8c6 in sout_InputSendBuffer (p_input=0x7fffc0000ad0, p_buffer=p_buffer@...ry=0x7fffdcde0650) at stream_output/stream_output.c:235 #4 0x00007ffff791b1ed in DecoderPlaySout (p_sout_block=0x7fffdcde0650, p_dec=0x7fffdd0a2828) at input/decoder.c:1467 #5 DecoderProcessSout (p_block=0x0, p_dec=0x7fffdd0a2828) at input/decoder.c:1523 #6 DecoderProcess (p_block=<optimized out>, p_dec=0x7fffdd0a2828) at input/decoder.c:1754 #7 DecoderThread (p_data=0x7fffdd0a2828) at input/decoder.c:896 #8 0x00007ffff6d4b0a4 in start_thread (arg=0x7fffc75a8700) at pthread_create.c:309 #9 0x00007ffff687bccd in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:111 ---- /snip ---- We also found the following two other functions that employ the same problematic programming pattern and should probably be patched as well: modules/services_discovery/sap.c: ---- snip ---- static sdp_t *ParseSDP (vlc_object_t *p_obj, const char *psz_sdp) { // ... /* TODO: use iconv and charset attribute instead of EnsureUTF8 */ while (*psz_sdp) { /* Extract one line */ char *eol = strchr (psz_sdp, '\n'); size_t linelen = eol ? (size_t)(eol - psz_sdp) : strlen (psz_sdp); char line[linelen + 1]; memcpy (line, psz_sdp, linelen); line[linelen] = '\0'; // ... } // ... } ---- /snip ---- modules/access/ftp.c: ---- snip ---- static int ftp_SendCommand( vlc_object_t *obj, access_sys_t *sys, const char *fmt, ... ) { size_t fmtlen = strlen( fmt ); char fmtbuf[fmtlen + 3]; memcpy( fmtbuf, fmt, fmtlen ); memcpy( fmtbuf + fmtlen, "\r\n", 3 ); // ... } ---- /snip ---- Potential buffer overflow in Dirac/Schroedinger Encoder ======================================================= The function Encode in modules/codec/dirac.c may allow an attacker to cause a buffer overflow on 32 bit systems. At (1), a raw 32 bit value is read using the macro GetDWBE and stored in the local variable 'len', which is then passed to malloc as a first argument at (2). However, before allocation, 'sizeof(eos)' is added to the length. If the length is chosen to be 0xffffffff, the summation will overflow, resulting in the allocation of a small buffer. Subsequently copying 'len' bytes into the small buffer can then cause an overflow at (3). ---- snip ---- static block_t *Encode( encoder_t *p_enc, picture_t *p_pic ) { // ... /* Presence of a Sequence header indicates a seek point */ if( 0 == p_block->p_buffer[4] ) { p_block->i_flags |= BLOCK_FLAG_TYPE_I; if( !p_enc->fmt_out.p_extra ) { const uint8_t eos[] = { 'B','B','C','D',0x10,0,0,0,13,0,0,0,0 }; uint32_t len = GetDWBE( p_block->p_buffer + 5 ); (1) // ... p_enc->fmt_out.p_extra = malloc( len + sizeof(eos)); //(2) if( !p_enc->fmt_out.p_extra ) return NULL; memcpy( p_enc->fmt_out.p_extra, p_block->p_buffer,len); //(3) memcpy( (uint8_t*)p_enc->fmt_out.p_extra + len, eos, sizeof(eos) ); SetDWBE( (uint8_t*)p_enc->fmt_out.p_extra + len + 10, len ); p_enc->fmt_out.i_extra = len + sizeof(eos); } } // ... } ---- /snip ---- The same code can be found in function Encode in modules/codec/schroedinger.c. We have not built a trigger for this issue, however, if it can be triggered, we suspect that it can be leveraged for arbitrary code execution just like the vulnerability in the MP4 demuxer. Minor issues (Null-pointer dereferences) ======================================= We also found the following minor issuesthat we believe can at most result in a null-pointer dereference and thus, a crash. For the sake of completeness, we report them as well. The allocations at (1)-(6) in the function TrackCreateES in /modules/demux/mp4/mp4.c are not checked, possibly resulting in subsequent null-pointer dereferences when calling memcpy in the respective next line. ---- snip ---- /* * TrackCreateES: * Create ES and PES to init decoder if needed, for a track starting at i_chunk */ static int TrackCreateES( demux_t *p_demux, mp4_track_t *p_track, unsigned int i_chunk, es_out_id_t **pp_es ) { // ... if( ( ( p_esds = MP4_BoxGet( p_sample, "esds" ) ) || ( p_esds = MP4_BoxGet( p_sample, "wave/esds" ) ) )&& ( p_esds->data.p_esds )&& ( p_decconfig ) ) { // ... if( p_track->fmt.i_extra > 0 ) { p_track->fmt.p_extra = malloc( p_track->fmt.i_extra ); // (1) memcpy( p_track->fmt.p_extra, p_decconfig->p_decoder_specific_info, p_track->fmt.i_extra ); } } else { // ... if( p_track->fmt.i_extra > 0 ) { p_track->fmt.p_extra = malloc( p_track->fmt.i_extra );//(2) memcpy( p_track->fmt.p_extra, p_sample->data.p_sample_vide->p_qt_image_description, p_track->fmt.i_extra); // (7) } break; // ... if( p_track->fmt.i_extra > 0 ) { p_track->fmt.p_extra = malloc( p_track->fmt.i_extra );//(3) memcpy( p_track->fmt.p_extra, p_sample->data.p_sample_soun->p_qt_description, p_track->fmt.i_extra); // (8) } if( p_track->fmt.i_extra == 56 && p_sample->i_type == VLC_CODEC_ALAC ) { p_track->fmt.audio.i_channels = *((uint8_t*)p_track->fmt.p_extra + 41); p_track->fmt.audio.i_rate = GetDWBE((uint8_t*)p_track->fmt.p_extra + 52); } break; case VLC_FOURCC( 'v', 'c', '-', '1' ): { MP4_Box_t *p_dvc1 = MP4_BoxGet( p_sample, "dvc1" ); if( p_dvc1 ) { p_track->fmt.i_extra = p_dvc1->data.p_dvc1->i_vc1; if( p_track->fmt.i_extra > 0 ) { p_track->fmt.p_extra = malloc( p_dvc1->data.p_dvc1->i_vc1 );//(4) memcpy( p_track->fmt.p_extra, p_dvc1->data.p_dvc1->p_vc1, p_track->fmt.i_extra ); } } else { msg_Err( p_demux, "missing dvc1" ); } break; } /* avc1: send avcC (h264 without annexe B, ie without start code)*/ case VLC_FOURCC( 'a', 'v', 'c', '1' ): { MP4_Box_t *p_avcC = MP4_BoxGet( p_sample, "avcC" ); if( p_avcC ) { p_track->fmt.i_extra = p_avcC->data.p_avcC->i_avcC; if( p_track->fmt.i_extra > 0 ) { p_track->fmt.p_extra = malloc( p_avcC->data.p_avcC->i_avcC );//(5) memcpy( p_track->fmt.p_extra, p_avcC->data.p_avcC->p_avcC, p_track->fmt.i_extra ); } } else { msg_Err( p_demux, "missing avcC" ); } break; } case VLC_FOURCC( 'h', 'v', 'c', '1' ): { MP4_Box_t *p_hvcC = MP4_BoxGet( p_sample, "hvcC" ); if( p_hvcC ) { p_track->fmt.i_extra = p_hvcC->data.p_hvcC->i_hvcC; if( p_track->fmt.i_extra > 0 ) { p_track->fmt.p_extra = malloc( p_hvcC->data.p_hvcC->i_hvcC );//(6) memcpy( p_track->fmt.p_extra, p_hvcC->data.p_hvcC->p_hvcC, p_track->fmt.i_extra ); } p_track->fmt.i_codec = VLC_CODEC_HEVC; } else { msg_Err( p_demux, "missing hvcC" ); } break; } // ... } } #undef p_decconfig if( pp_es ) *pp_es = es_out_Add( p_demux->out, &p_track->fmt ); return VLC_SUCCESS; } ---- /snip ---- In function EncoderSetAudioType in modules/codec/dmo/dmo.c, the allocation at (1) is not checked, possibly resulting a null-pointer dereference in the subsequent call to memcpy. static int EncoderSetAudioType( encoder_t *p_enc, IMediaObject *p_dmo ) { // ... if( p_wf->cbSize ) { msg_Dbg( p_enc, "found cbSize: %i", p_wf->cbSize ); p_enc->fmt_out.i_extra = p_wf->cbSize; p_enc->fmt_out.p_extra = malloc(p_enc->fmt_out.i_extra );//(1) memcpy( p_enc->fmt_out.p_extra, &p_wf[1], p_enc->fmt_out.i_extra ); } // ... }
Powered by blists - more mailing lists
Please check out the Open Source Software Security Wiki, which is counterpart to this mailing list.
Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.