Bug ID 1073008
Summary VUL-0: CVE-2017-17670: CVE-2017-17670: vlc: type conversion vulnerability
Classification openSUSE
Product openSUSE Distribution
Version Leap 42.3
Hardware Other
URL https://smash.suse.de/issue/196721/
OS Other
Status NEW
Severity Normal
Priority P5 - None
Component Other
Assignee dimstar@opensuse.org
Reporter meissner@suse.com
QA Contact security-team@suse.de
Found By Security Response Team
Blocker ---

CVE-2017-17670

http://seclists.org/oss-sec/2017/q4/396


 CVE-2017-17670: vlc: type conversion vulnerability From: Hans Jerry Illikainen
<hji () dyntopia com>
Date: Fri, 15 Dec 2017 01:32:11 +0000

About
=====

A type conversion vulnerability exist in the MP4 demux module in VLC
<=2.2.8.  This issue has been assigned CVE-2017-17670 and it could be
used to cause an arbitrary free.


Details
=======

MP4 is a container format for video, audio, subtitles and images.  The
various parts of an .mp4 are organized as hierarchical boxes/atoms in
big-endian byte ordering [1].

VLC processes these boxes by using a lookup table:

vlc-2.2.8/modules/demux/mp4/libmp4.c
,----
| 3297 static const struct
| 3298 {
| 3299     uint32_t i_type;
| 3300     int  (*MP4_ReadBox_function )( stream_t *p_stream, MP4_Box_t *p_box
);
| 3301     void (*MP4_FreeBox_function )( MP4_Box_t *p_box );
| 3302     uint32_t i_parent; /* set parent to restrict, duplicating if needed;
0 for any */
| 3303 } MP4_Box_Function [] =
| 3304 {
| 3305     /* Containers */
| 3306     { ATOM_moov,    MP4_ReadBoxContainer,     MP4_FreeBox_Common, 0 },
| 3307     { ATOM_trak,    MP4_ReadBoxContainer,     MP4_FreeBox_Common,
ATOM_moov },
| ....
| 3565     /* Last entry */
| 3566     { 0,              MP4_ReadBox_default,      NULL, 0 }
| 3567 };
`----

vlc-2.2.8/modules/demux/mp4/libmp4.c
,----
| 3574 static MP4_Box_t *MP4_ReadBox( stream_t *p_stream, MP4_Box_t *p_father )
| 3575 {
| 3576     MP4_Box_t *p_box = calloc( 1, sizeof( MP4_Box_t ) ); /* Needed to
ensure simple on error handler */
| 3577     unsigned int i_index;
| ....
| 3582     if( !MP4_ReadBoxCommon( p_stream, p_box ) )
| 3583     {
| ....
| 3587     }
| ....
| 3605     /* Now search function to call */
| 3606     for( i_index = 0; ; i_index++ )
| 3607     {
| ....
| 3613         if( ( MP4_Box_Function[i_index].i_type == p_box->i_type )||
| 3614             ( MP4_Box_Function[i_index].i_type == 0 ) )
| 3615         {
| 3616             break;
| 3617         }
| 3618     }
| 3619
| 3620     if( !(MP4_Box_Function[i_index].MP4_ReadBox_function)( p_stream,
p_box ) )
| 3621     {
| 3622         MP4_BoxFree( p_stream, p_box );
| 3623         return NULL;
| 3624     }
| 3625
| 3626     return p_box;
| 3627 }
`----


MP4_ReadBox() allocates a MP4_Box_t structure and invokes
MP4_ReadBoxCommon() to read the properties common to all mp4 boxes;
`i_size' and `i_type' (and optionally an extended size).  Afterwards,
MP4_Box_Function is used to dispatch further parsing to a suitable
function based on its `i_type'.

When VLC is done with the boxes, they are freed with MP4_BoxFree():

vlc-2.2.8/modules/demux/mp4/libmp4.c
,----
| 3633 void MP4_BoxFree( stream_t *s, MP4_Box_t *p_box )
| 3634 {
| 3635     unsigned int i_index;
| ....
| 3650     /* Now search function to call */
| 3651     if( p_box->data.p_payload )
| 3652     {
| 3653         for( i_index = 0; ; i_index++ )
| 3654         {
| ....
| 3660             if( ( MP4_Box_Function[i_index].i_type == p_box->i_type )||
| 3661                 ( MP4_Box_Function[i_index].i_type == 0 ) )
| 3662             {
| 3663                 break;
| 3664             }
| 3665         }
| 3666         if( MP4_Box_Function[i_index].MP4_FreeBox_function == NULL )
| 3667         {
| ....
| 3677         }
| 3678         else
| 3679         {
| 3680             MP4_Box_Function[i_index].MP4_FreeBox_function( p_box );
| 3681         }
| ....
| 3685 }
`----

Again, `i_type' is used to find a suitable free-function.

The reason this may be problematic is that `i_type' could be changed
when VLC handles `sinf' and `frma' boxes in TrackCreateES() -- meaning
that a box may be read as one type, and freed as another.

`sinf' is the "Protection Scheme Information Box" and it's used for
protected/encrypted media.  `frma' is the "Original Format Box" and it's
used to declare the format of the unprotected media.

If a sinf/frma is found underneath a sample box, the `i_type' of that
sample is replaced with the original format declared in the `frma':

vlc-2.2.8/modules/demux/mp4/mp4.c
,----
| 2180 static int TrackCreateES( demux_t *p_demux, mp4_track_t *p_track,
| 2181                           unsigned int i_chunk, es_out_id_t **pp_es )
| 2182 {
| ....
| 2208     p_sample = MP4_BoxGet(  p_track->p_stsd, "[%d]",
| 2209                             i_sample_description_index - 1 );
| ....
| 2219     p_track->p_sample = p_sample;
| 2220
| 2221     if( ( p_frma = MP4_BoxGet( p_track->p_sample, "sinf/frma" ) ) &&
p_frma->data.p_frma )
| 2222     {
| 2223         msg_Warn( p_demux, "Original Format Box: %4.4s", (char
*)&p_frma->data.p_frma->i_type );
| 2224
| 2225         p_sample->i_type = p_frma->data.p_frma->i_type;
| 2226     }
| ....
`----

No sanity check is done to make sure that the MP4_FreeBox_function
associated with the new `i_type' is compatible with the old
MP4_ReadBox_function.


Example
=======

One way to abuse the type change is to have a `soun' changed to a
`vide'.  This results in a 72-byte allocation (x86-64) for the
`p_sample_soun' member of the p_box->data union when the box is read:

vlc-2.2.8/modules/demux/mp4/libmp4.c
,----
| 1614 static int MP4_ReadBox_sample_soun( stream_t *p_stream, MP4_Box_t *p_box
)
| 1615 {
| 1616     p_box->i_handler = ATOM_soun;
| 1617     MP4_READBOX_ENTER( MP4_Box_data_sample_soun_t );
| ....
`----

vlc-2.2.8/modules/demux/mp4/libmp4.h
,----
| 1351 #define MP4_READBOX_ENTER( MP4_Box_data_TYPE_t ) \
| ....
| 1369     if( !( p_box->data.p_payload = calloc( 1, sizeof(
MP4_Box_data_TYPE_t ) ) ) ) \
| 1370     { \
| ....
| 1373     }
`----

where `p_box' is MP4_Box_t:

vlc-2.2.8/modules/demux/mp4/libmp4.h
,----
| 1284 typedef struct MP4_Box_s
| 1285 {
| ....
| 1296     MP4_Box_data_t   data;   /* union of pointers on extended data
depending
| 1297                                 on i_type (or i_usertype) */
| ....
| 1306 } MP4_Box_t;
`----

and MP4_Box_data_t:

vlc-2.2.8/modules/demux/mp4/libmp4.h
,----
| 1200 typedef union MP4_Box_data_s
| 1201 {
| ....
| 1220     MP4_Box_data_sample_vide_t *p_sample_vide;
| 1221     MP4_Box_data_sample_soun_t *p_sample_soun;
| ....
| 1278     void                *p_payload; /* for unknow type */
| 1279 } MP4_Box_data_t;
`----

,----
| (gdb) p sizeof(MP4_Box_data_sample_soun_t)
| $1 = 72
`----

After the box has had its type changed to `vide' and it's later freed,
the `p_sample_vide' member of the p_box->data union is used:

vlc-2.2.8/modules/demux/mp4/libmp4.c
,----
| 1861 void MP4_FreeBox_sample_vide( MP4_Box_t *p_box )
| 1862 {
| 1863     FREENULL( p_box->data.p_sample_vide->p_qt_image_description );
| 1864 }
`----

,----
| (gdb) p sizeof(MP4_Box_data_sample_vide_t)
| $2 = 96
| (gdb)
`----

vlc-2.2.8/modules/demux/mp4/libmp4.h
,----
| 529 typedef struct MP4_Box_data_sample_vide_s
| 530 {
| ...
| 557     uint8_t *p_qt_image_description;
| 558
| 559 } MP4_Box_data_sample_vide_t;
`----

`p_sample_vide' is 24 bytes larger than `p_sample_soun', and
`p_qt_image_description' is at the end of the vide struct; i.e. the
pointer to be free()d is read out-of-bounds from potentially
user-controlled memory.

`mkmp4.py' at [2]

,----
| $ uname -imrs
| FreeBSD 11.1-RELEASE-p4 amd64 GENERIC
| $ ./mkmp4.py file.mp4
| $ vlc --version
| VLC media player 2.2.8 Weatherwax (revision 2.2.7-14-g3cc1d8cba9)
| $ gdb -q --args vlc file.mp4
| (gdb) set breakpoint pending on
| (gdb) b libmp4.c:1618
| No source file named libmp4.c.
| Breakpoint 1 (libmp4.c:1618) pending.
| (gdb) b libmp4.c:1863
| No source file named libmp4.c.
| Breakpoint 2 (libmp4.c:1863) pending.
| (gdb) r
| [...]
| Breakpoint 3, MP4_ReadBox_sample_soun (p_stream=0x802ab2710,
p_box=0x802a85000) at demux/mp4/libmp4.c:1618
| 1618        p_box->data.p_sample_soun->p_qt_description = NULL;
| (gdb) p p_box->data.p_sample_soun
| $1 = (MP4_Box_data_sample_soun_t *) 0x802a79810
| (gdb) c
| Continuing.
|
| Breakpoint 4, MP4_FreeBox_sample_vide (p_box=0x802a85000) at
demux/mp4/libmp4.c:1863
| 1863        FREENULL( p_box->data.p_sample_vide->p_qt_image_description );
| (gdb) p p_box->data.p_sample_vide
| $2 = (MP4_Box_data_sample_vide_t *) 0x802a79810
| (gdb) p p_box->data.p_sample_vide->p_qt_image_description
| $3 = (uint8_t *) 0x1122334455667788 <Error reading address
0x1122334455667788: Bad address>
| (gdb) b free
| Breakpoint 5 at 0x8019d3ce4
| (gdb) c
| Continuing.
|
| Breakpoint 5, 0x00000008019d3ce4 in free () from /lib/libc.so.7
| (gdb) p/x $rdi
| $4 = 0x1122334455667788
| (gdb) c
| Continuing.
|
| Program received signal SIGBUS, Bus error.
| 0x00000008019d36f3 in realloc () from /lib/libc.so.7
| (gdb) x/i $rip
| 0x8019d36f3 <realloc+3939>:     mov    rbx,QWORD PTR [rax+rcx*8+0x68]
| (gdb) i r
| rax            0x1122334455600000       1234605616436084736
| rbx            0x1122334455667788       1234605616436508552
| rcx            0x5a     90
| [...]
| (gdb) bt 4
| #0  0x00000008019d36f3 in realloc () from /lib/libc.so.7
| #1  0x00000008019d3d51 in free () from /lib/libc.so.7
| #2  0x0000000806d7fafd in MP4_FreeBox_sample_vide (p_box=0x802a85000) at
demux/mp4/libmp4.c:1863
| #3  0x0000000806d7fcfd in MP4_BoxFree (s=0x802ab2710, p_box=0x802a85000) at
demux/mp4/libmp4.c:3680
`----


Solution
========

This issue does not affect the HEAD of the VLC master branch.



Footnotes
_________

[1] [http://xhelmboyx.tripod.com/formats/mp4-layout.txt]

[2] [https://gist.github.com/dyntopia/194d912287656f66dd502158b0cd2e68]


-- 
hji


You are receiving this mail because: