Update Aug 28, 2015: Typos in the final table: CVE-2015-3864 does not concern covr but tx3g. CVE-2015-3828 does not occur for yrrc.
Detecting the PoCs published by Zimperium is not difficult: you can fingerprint the PoCs, for example.
Detecting variants of the PoCs, i.e., MP4s that use one of the discovered vulnerabilities, is far more difficult. I'll explain why in a moment.
First, apart from here (in Chinese), there hasn't been so much in the way of technical details. Getting into the guts of StageFright really had me sweating. I'm not even sure I understand everything yet.
An MPEG-4 is made of several units called 'atoms' or 'boxes'. Those boxes begin with a header, as detailed below: a size, and a box type. The box types consist of a four character code (FourCC) such as 'covr', 'esds', etc. It's while reading those boxes that Android's mediaserver sometimes encounters vulnerabilities.
Box Type Description albm Album title and track number for the media auth Author of the media covr Album cover artwork ctss Composition Time To Sample mapping esds Elemetary Stream Description gnre genre of the media perf performer or artist stsc Sample To Chunk mapping stss List of Sync Samples stts Time To Sample mapping tx3g Text metadata yrrc Recording year for the media
The first error I made was to assume all PoCs would make the Android mediaserver crash. Several PoCs (number 1, 5, 7 and 8) do not crash a number of emulators/devices running Android 4.4.2 or 5.1.1.
This does not mean, however, that the issue is not there.
PoC filename CVE-2015-.... Description Crash? sf-001.mp4 1538 stsc integer overflow No sf-002.mp4 1538 ctts integer overflow Yes sf-003.mp4 1538 stts integer overflow Yes sf-004.mp4 1538 stss integer overflow Yes sf-005.mp4 1539 esds integer underflow No sf-006.mp4 3827 covr integer underflow Yes sf-007.mp4 3826 3gpp buffer overread No sf-008.mp4 3828 3gpp integer underflow No sf-009.mp4 3824 tx3g integer overflow Yes sf-010.mp4 3829 covr integer overflow Yes
It's not because it crashes (in StageFright) that it's a "StageFright" vulnerability.
During our investigations, we were surprised to see libstagefright crash in unexpected situations with a modified sf-003.mp4.
sf-003.mp4 demonstrates an integer overflow while reading a stts box. Let's first explain when we expect the overflow to occur by analyzing the corresponding patch:
- uint64_t allocSize = numEntries * 2 * sizeof(uint32_t);
+ uint64_t allocSize = numEntries * 2 * (uint64_t)sizeof(uint32_t);
As sizeof(uint32_t) is 4, an integer overflow would occur if
allocSize = numEntries * 2 * 4 >= 0xFFFFFFFF + 1
i.e numEntries >= (0xFFFFFFFF + 1) / 8 = 0x20000000
So, in other terms, any MP4 file with a 'stts' box containing numEntries >= 0x20000000 will trigger the issue. An 'stts' box is a box that "defines time-to-sample mapping for a sample table". It is structured as below:
numEntries in Android's code corresponds to the Count field. So, this means that if bytes 5,6,7,8 after 'stts' are greater than or equal to 20 00 00 00, the overflow occurs.
F/libc ( 1482): Fatal signal 11 (SIGSEGV) at 0x10958a14 (code=1), thread 1490 (Binder_2)
I/DEBUG ( 1011): backtrace:
I/DEBUG ( 1011): #00 pc 0007df3a /system/lib/libstagefright.so (android::SampleTable::setTimeToSampleParams(long long, unsigned int)+133)
I/DEBUG ( 1011): #01 pc 000630fb /system/lib/libstagefright.so (android::MPEG4Extractor::parseChunk(long long*, int)+3262)
Yet, as I said, we were surprised to see libstagefright crash with lesser values such as 0x1f ff ff ff!
Have a look at the crash log though.
F/libc ( 1791): Fatal signal 6 (SIGABRT) at 0x000006ff (code=-6), thread 1799 (Binder_3)
/DEBUG ( 55): backtrace:
I/DEBUG ( 55): #00 pc 00021e20 /system/lib/libc.so (tgkill+12)
I/DEBUG ( 55): #01 pc 00012ea9 /system/lib/libc.so (pthread_kill+48)
I/DEBUG ( 55): #02 pc 000130bd /system/lib/libc.so (raise+10)
I/DEBUG ( 55): #03 pc 00011df3 /system/lib/libc.so
I/DEBUG ( 55): #04 pc 000216d4 /system/lib/libc.so (abort+4)
I/DEBUG ( 55): #05 pc 00000911 /system/lib/libstdc++.so (operator new(unsigned int)+8)
I/DEBUG ( 55): #06 pc 0007df0f /system/lib/libstagefright.so (android::SampleTable::setTimeToSampleParams(long long, unsigned int)+90)
I/DEBUG ( 55): #07 pc 000630fb /system/lib/libstagefright.so (android::MPEG4Extractor::parseChunk(long long*, int)+3262)
It is different:
This is a Fatal signal 6 (SIGABRT), not a SIGSEGV
The crash occurs when allocating a buffer. Specifically, it crashes on line number 337 in the file SampleTable.cpp:
mTimeToSample = new uint32_t[mTimeToSampleCount * 2];
So what's that? It's a bug. From an end user point of view, it can be seen as a denial of service, as the mediaserver crashes. But it's not an integer overflow you can use to write the memory with a custom exploit.
$ mp4file --dump sf-007.mp4
"sf-007.mp4": Dumping meta-information...
"sf-007.mp4": type ftyp (ftyp)
"sf-007.mp4": majorBrand = mp42
"sf-007.mp4": minorVersion = 0 (0x00000000)
"sf-007.mp4": <table entries suppressed>
"sf-007.mp4": type mdat (mdat)
This article did not really explain when the issue occurs, so I will.
This is an integer underflow when parsing 3GPP Metadata. 3GPP Metadata correspond to boxes 'titl' (title), 'auth' (author), 'gnre' (genre), 'perf' (performers) and 'albm' (album).
The patch shows that, in parse3GPPMetaData, an underflow occurred if size < 6
+ if (size < 6)
+ return ERROR_MALFORMED;
Beware: size here is not the box size, but the 3GPP Metadata size (chunk_data_size).
status_t err = parse3GPPMetaData(data_offset, chunk_data_size, depth);
Compared to the chunk data size, the box size is chunk_data_size + 4 (box size) + 4 (box type).
So, the underflow occurs if the box size < 6 + 8 = 14 (0x0E). Indeed, the PoC sf-008.mp4 shows an 'albm' box with a size of 0x0D.
Note that if the box uses an extended size form, the condition where the underflow occurs is slightly changed, because the box size is chunk_data_size + 4 (extended size indicator) + 4 (box type) + 8 (extended size).
This is a vulnerability I investigated for quite a long time because I couldn't find proper documentation. In case it can help you, I think I worked out the format of the 'esds' box.
The 'esds' atom refers to 'elementary stream descriptors'.
Its format is detailed this book.
So, we can work out the format of an esds box is:
CVE Description Hex 1538 stsc integer overflow
mNumSampleToChunkOffsets are in dark blue and cause the
1538 ctts integer overflow
numEntries are in dark blue and cause the overflow
1538 stts integer overflow
mTimeToSampleCount is in dark blue and causes the overflow
1538 stss integer overflow
mNumSyncSamples are in dark blue and causes the overflow
1539 esds integer underflow
Box type is in yellow, ESD size is in violet, ES_ID is in light green,
flags are in red.
The combination of the size and flags cause the underflow
3827 covr integer underflow
The box size is the 4 bytes before the 'covr' box type and causes the
3826 titl, albm, perf, auth, gnre buffer overread
The 3GPP string is in dark blue.
The format for 3GPP metadata fields can be found here (page 31)
3828 titl, albm, perf, auth, gnre integer underflow
The box size are the 4 bytes before the 'albm' box type .
3824 tx3g integer overflow
The box sizes are in red. Their sum causes an integer overflow in the
3829 covr integer overflow
The box size is set to 1 (orange) which means there is an extended
box size (in red) - leading to an integer overflow in the code
3864 tx3g integer overflow
An integer overflow actually occurs whenever an extended box size is used with chunk_size > 0x1ffffffff
Thanks to Neo Tan, Ruchna Nigam, Roland Dela Paz for their contribution to this work.
-- the Crypto Girl