Technical analysis: Android libStagefright Vulnerability Analysis
The article corresponds to the CVE-2015-{1538,1539, 3824,3826, 3827,3828, 3829} 7 CVE, the specific ing relationship is unknown. This vulnerability affects the security of Android 95%. By following up on the attack surface of this vulnerability, this statement is no exaggeration. It is also credible to say that a MMS Message is directly used to lay down the machine. However, this is only one of the many attacks.
Attack Surface Analysis
LibStagefright is used by mediaserver by default. That is to say, if a malicious video file is processed by mediaserver, this vulnerability can be triggered. For example:
For example, if a file management app stores a video in an sdcard, open the file management app and drop down the list to expose the video. The thumbnail resolution is triggered, and the vulnerability triggers the image library app, click a local image and a thumbnail will appear. If the video is in the sdcard or download directory, this will also be triggered.
Also affected. After you click a video, the media server crashes. In addition, even if the user does not click the received video and then sends the image in the middle, the same effect of the front gallery and file manager will also trigger the thumbnail process and overflow.
Open a video link (mp4) in the latest version of Chrome43 without clicking auto-trigger.
Boot is also a trigger point. mediaprovider scans all files in the SD card and tries to parse them.
The architecture of media framework is as follows: Programs developed using the android media framework will be affected.
What I want to talk about here is that, if we disable the MMS function, we will seek psychological comfort. From the perspective of the root cause, most (one exception) are related to the overflow/underflow of integer computing. This problem indirectly leads to subsequent security problems such as memory corruption.
Code Analysis
1.1.1. No1 heap read out of bounds
status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) {uint32_t hdr[2];uint64_t chunk_size = ntohl(hdr[0]);uint32_t chunk_type = ntohl(hdr[1]);switch(chunk_type) {
Only the following chunk_types will trigger the branch parse3GPPMetaData:
case FOURCC('t', 'i', 't', 'l'):case FOURCC('p', 'e', 'r', 'f'):case FOURCC('a', 'u', 't', 'h'):case FOURCC('g', 'n', 'r', 'e'):case FOURCC('a', 'l', 'b', 'm'):case FOURCC('y', 'r', 'r', 'c'): {*offset += chunk_size;status_t err = style="color: #ff0000;">parse3GPPMetaData(data_offset, chunk_data_size, depth);if (err != OK) {return err; }break;}
The preceding parse3GPPMetaData triggers two 3gp vulnerabilities. The first setCString heap reads out of bounds. First, the size data is read from the file offset to the buffer.
status_t MPEG4Extractor::parse3GPPMetaData(off64_t offset, size_t size, int depth) {if (size style="color: #ff0000;">if (mDataSource->readAt(style="color: #ff0000;">offset, buffer, size) != (ssize_t)size) {delete[] buffer;buffer = NULL;return ERROR_IO;}
Then, this is similar to strcpy, so it is
mFileMetaData->setCString(metadataKey, (const char *)buffer + 6);https://android.googlesource.com/platform/frameworks/av/+/android-5.1.1_r8/media/libstagefright/MetaData.cppbool MetaData::setCString(uint32_t key, const char *value) {return setData(key, TYPE_C_STRING, value, style="color: #ff0000;">strlen(value) + 1);}bool MetaData::setData(uint32_t key, uint32_t type, const void *data, size_t size) {bool overwrote_existing = true;ssize_t i = mItems.indexOfKey(key);if (i
It is noted that the size is dynamic, so it generally does not overflow, but the read out-of-bounds occurs.
void MetaData::typed_data::setData(uint32_t type, const void *data, size_t size) {clear();mType = type;style="color: #ff0000;">allocateStorage(size);memcpy(storage(), data, size);}
The read content is stored in a metadata file and may be leaked (such as the title and artist information)
1.1.2. No2 heap Overwrite
The second is the under flow, if the size
if (metadataKey > 0) {bool isUTF8 = true; // Common casechar16_t *framedata = NULL; int len16 = 0; // Number of UTF-16 characters// smallest possible valid UTF-16 string w BOM: 0xfe 0xff 0x00 0x00 if (size - 6 >= 4) {len16 = ((size - 6) / 2) - 1; // don't include 0x0000 terminatorframedata = (char16_t *)(buffer + 6);if (0xfffe == *framedata) {// endianness marker (BOM) doesn't match host endiannessfor (int i = 0; i style="color: #ff0000;">bswap_16(framedata[i]);} // BOM is now swapped to 0xfeff, we will execute next block too}
According to the previous calculation, the size here is chunk_data_size, which indicates the data size except the header in the chunk. The calculation method is as follows: off64_t data_offset = * offset + 8; During header parsing, offsetoff64_t chunk_data_size = * offset + chunk_size-data_offset; therefore, chunk_size8 can be used. Chunk_size comes from the first 4 bytes of the file tag. 1.1.3. No3 heap overflow
Next is the mpeg tx3g tag, and the chunk_size is a uint. The sum of the chunk_size and size overflows, resulting in memory with a small actual proportion. The data written to memcpy heap overflow should be controllable.
1.1.4. No4 heap out-of-range read
When chunk_data_size is smaller than kSkipBytesOfDataBox in covr tag processing, setData will read the boundary of the buffer. Because setData allocates memory, but most of the allocation fails, it may also cause writing to the memory with the address 0.
1.1.5. No5 heap Overwrite
When chunk_data_size = SIZE_MAX, + 1 will allocate 0-length memory, and the subsequent readAt will write the buffer while reading the file. heap write is out of bounds until the end of reading the file. Because the covered data comes from files, the content and length are controllable.
1.1.6. No6 Integer overflow
When processing stsc tags, The setSampleToChunkParams method is called.
case FOURCC('s', 't', 's', 'c'):{status_t err =mLastTrack->sampleTable->setSampleToChunkParams(data_offset, chunk_data_size);*offset += chunk_size;if (err != OK) {return err;}break;}
This method contains integer overflow, which is mainly used in the loop process and is similar to mSampleToChunkEntries [I]. when startChunk is used, it is actually calculated based on I * sizeof (SampleToChunkEntry) + offset (startChunk). overflow may occur, but it does not necessarily cause memory corruption, may interfere with the execution logic.
Https://android.googlesource.com/platform/frameworks/av/+/android-5.1.1_r8/media/libstagefright/SampleTable.cpp
mSampleToChunkEntries = new SampleToChunkEntry[mNumSampleToChunkOffsets]; for (uint32_t i = 0; i readAt(mSampleToChunkOffset + 8 + i * 12, buffer, sizeof(buffer)) != (ssize_t)sizeof(buffer)) { return ERROR_IO; } CHECK(U32_AT(buffer) >= 1); // chunk index is 1 based in the spec. // We want the chunk index to be 0-based. mSampleToChunkEntries[i].startChunk = U32_AT(buffer) - 1; mSampleToChunkEntries[i].samplesPerChunk = U32_AT(&buffer[4]);mSampleToChunkEntries[i].chunkDesc = U32_AT(&buffer[8]);}
Therefore, the patch adds verification.
+ If (SIZE_MAX/sizeof (SampleToChunkEntry)
1.1.7. No7 parseESDescriptor Integer overflow
The main problem here is that only check the size> = 3, then-2,-, and then continue several times-2,-length cannot guarantee not to overflow.
status_t ESDS::parseESDescriptor(size_t offset, size_t size) { if (size style="color: #ff0000;">size -= 2; }if (URL_Flag) { if (offset >= size) { return ERROR_MALFORMED;}unsigned URLlength = mData[offset];offset += URLlength + 1;style="color: #ff0000;">size -= URLlength + 1; }if (OCRstreamFlag) {offset += 2; style="color: #ff0000;"> size -= 2; if ((offset >= size || mData[offset] != kTag_DecoderConfigDescriptor)&& offset - 2 = size) { return ERROR_MALFORMED; } uint8_t tag;size_t sub_offset, sub_size; status_t err = style="color: #ff0000;">skipDescriptorHeader(offset, size, &tag, &sub_offset, &sub_size);
Although the overflow size is not directly seen, it may cause the developer's unexpected logic.
do {if (size == 0) { return ERROR_MALFORMED;} uint8_t x = mData[offset++];--size;*data_size = (*data_size
1.1.8. No8 SampleTable Integer overflow
Https://android.googlesource.com/platform/frameworks/av/+/android-5.1.1_r8/media/libstagefright/SampleTable.cpp
Multiply 32-bit uint and convert the result to 64-bit uint
uint32_t mTimeToSampleCount;mTimeToSampleCount = U32_AT(&header[4]);uint64_t allocSize = mTimeToSampleCount * 2 * sizeof(uint32_t); if (allocSize > SIZE_MAX) {return ERROR_OUT_OF_RANGE;}
There is an overflow problem here. Although no direct impact is seen, it may lead to misjudgment in subsequent checks. The solution is as follows:
-Uint64_t allocSize = mTimeToSampleCount * 2 * sizeof (uint32_t );
+ Uint64_t allocSize = mTimeToSampleCount * 2 * (uint64_t) sizeof (uint32_t );
Summary
The first 1-8 vulnerabilities have similarities.
No1: A piece of data is computed strlen, and then allocated memory and strcpy, but this data segment does not necessarily end with '\ 0', so the read is out of bounds.
No2-5: all four bytes before the tag size is not verified, can be any value, resulting in a series of size calculation problems. As shown in, all tag4 bytes are preceded by a 4-byte size:
No6-8: all are integer overflow, but no error of Direct Memory Corruption is seen. Data exceptions may occur.
POC
The trigger path for the Five vulnerabilities from 1 to 5 is very clear. When parseChunk encounters a special tag, the branch processing logic is faulty. Therefore, you only need to modify the corresponding tag to construct the poc.
In particular, 2-4 is the four-byte size before the tag. You only need to adjust the corresponding size.
The following POC is for no3. change one of the trak tags to tx3g, and then change the previous size to four FF
20:16:10-28. 888: I/DEBUG (247 ): **************************************** ********
07-28 20:16:10. 888: I/DEBUG (247): Build fingerprint: 'xiaomi/cancro: 4.4.4/KTU84P/4.8.22: user/release-keys'
07-28 20:16:10. 888: I/DEBUG (247): Revision: '0'
Style = "color: # ff0000;"> 20:16:10-28. 888: I/DEBUG (247): pid: 10928, tid: 10945, name: Binder_4 >>>/system/bin/mediaserver
07-28 20:16:10. 978: I/DEBUG (247): r0 00000000 r1 63707274 r2 b187a6e8 r3 00000000
07-28 20:16:10. 978: I/DEBUG (247): AM write failure (32/Broken pipe)
07-28 20:16:10. 978: I/DEBUG (247): r4 b187a6f8 r5 00000000 r6 b8100ed0 r7 b187aa28
07-28 20:16:10. 978: I/DEBUG (247): r8 74783367 r9 b66dc904 sl 201700a9 fp 00000000
07-28 20:16:10. 978: I/DEBUG (247): ip b66267b7 sp b187a690 lr b6df08df pc b664450e cpsr 60010030
07-28 20:16:10. 978: I/DEBUG (247): d0 0000000000000000 d1 0000000000000000
07-28 20:16:10. 978: I/DEBUG (247): d2 0000000000000000 d3 0000000000000000
07-28 20:16:10. 978: I/DEBUG (247): d4 3fd1cb8765719d59 d5 bebbbb3f58eabe9c
07-28 20:16:10. 978: I/DEBUG (247): d6 3e66376972bea4d0 d7 3ecccccd3ecccccd
07-28 20:16:10. 978: I/DEBUG (247): d8 0000000000000000 d9 0000000000000000
07-28 20:16:10. 978: I/DEBUG (247): d10 0000000000000000 d11 0000000000000000
07-28 20:16:10. 978: I/DEBUG (247): d12 0000000000000000 d13 0000000000000000
07-28 20:16:10. 978: I/DEBUG (247): d14 0000000000000000 d15 0000000000000000
07-28 20:16:10. 978: I/DEBUG (247): d16 3930373039303032 d17 2e37343932373154
07-28 20:16:10. 978: I/DEBUG (247): d18 006900640065006d d19 004d0049002e0061
07-28 20:16:10. 978: I/DEBUG (247): d20 0061006900640065 d21 00790061006c0050
07-28 20:16:10. 978: I/DEBUG (247): d22 006c004300720065 d23 0074006e00650069
07-28 20:16:10. 978: I/DEBUG (247): d24 3f77ff86776316e9 d25 bf77ff86919d591e
07-28 20:16:10. 978: I/DEBUG (247): d26 3fe1_000000000 d27 4000000000000000
07-28 20:16:10. 978: I/DEBUG (247): d28 3ffe542fa9d0152a d29 bfbcb8765719d592
07-28 20:16:10. 978: I/DEBUG (247): d30 3ff0000000000000 d31 3fd1cb8765719d59
07-28 20:16:10. 978: I/DEBUG (247): scr 20000010
07-28 20:16:10. 978: I/DEBUG (247): backtrace:
20:16:10-28. 978: I/DEBUG (247): style = "color: # ff0000;"> #00 pc 0006846e/system/lib/libstagefright. so (android: MPEG4Extractor: parseChunk (long *, int) + 4345)
20:16:10-28. 978: I/DEBUG (247): #01 pc 000675fb/system/lib/libstagefright. so (android: MPEG4Extractor: parseChunk (long *, int) + 646)
07-28 20:16:10. 978: I/DEBUG (247): #02 pc 00068a8b/system/lib/libstagefright. so (android: MPEG4Extractor: readMetaData () + 46)
07-28 20:16:10. 978: I/DEBUG (247): #03 pc 00068d31/system/lib/libstagefright. so (android: MPEG4Extractor: countTracks () + 4)
20:16:10-28. 978: I/DEBUG (247): #04 pc 00092077/system/lib/libstagefright. so (android: ExtendedUtils: MediaExtractor_CreateIfNeeded (android: sp, android: sp const &, char const *) + 206)
20:16:10-28. 978: I/DEBUG (247): #05 pc 00075a43/system/lib/libstagefright. so (android: MediaExtractor: Create (android: sp const &, char const *) + 566)
20:16:10-28. 978: I/DEBUG (247): #06 pc 0005a00b/system/lib/libstagefright. so (android: AwesomePlayer: setperformance_l (android: sp const &) + 10)
20:16:10-28. 978: I/DEBUG (247): #07 pc 0005b519/system/lib/libstagefright. so (android: AwesomePlayer: setDataSource (int, long, long) + 136)
20:16:10-28. 978: I/DEBUG (247): #08 pc 00034319/system/lib/libmediaplayerservice. so (android: MediaPlayerService: Client: setDataSource (int, long, long) + 196)
20:16:10-28. 978: I/DEBUG (247): style = "color: # ff0000;"> #09 pc 00059b2d/system/lib/libmedia. so (android: BnMediaPlayer: onTransact (unsigned int, android: Parcel const &, android: Parcel *, unsigned int) + 332)
20:16:10-28. 978: I/DEBUG (247): style = "color: # ff0000;"> #10 pc 00019225/system/lib/libbinder. so (android: BBinder: transact (unsigned int, android: Parcel const &, android: Parcel *, unsigned int) + 60)
07-28 20:16:10. 978: I/DEBUG (247): #11 pc 0001d799/system/lib/libbinder. so (android: IPCThreadState: executeCommand (int) + 508)
07-28 20:16:10. 978: I/DEBUG (247): #12 pc 0001db17/system/lib/libbinder. so (android: IPCThreadState: getAndExecuteCommand () + 38)
07-28 20:16:10. 978: I/DEBUG (247): #13 pc 0001db8d/system/lib/libbinder. so (android: IPCThreadState: joinThreadPool (bool) + 48)
07-28 20:16:10. 978: I/DEBUG (247): #14 pc 000219f5/system/lib/libbinder. so
20:16:10-28. 978: I/DEBUG (247): #15 pc release ea5d/system/lib/libutils. so (android: Thread: _ threadLoop (void *) + 216)
07-28 20:16:10. 978: I/DEBUG (247): #16 pc release e58f/system/lib/libutils. so
07-28 20:16:10. 978: I/DEBUG (247): #17 pc release d248/system/lib/libc. so (_ thread_entry + 72)
07-28 20:16:10. 978: I/DEBUG (247): #18 pc worker d3e0/system/lib/libc. so (pthread_create + 240)
From the trace, we can see that the binder is used to call the interface provided by media server, and thus the video parsing process crashes. So it overflows in the media server process.
Protection
Media is a very core service in Android (although its permissions are not high). Many features involve this service. If you only stop media to stop the service, the mobile phone is basically unavailable, for example, the desktop cannot be displayed.
Service media/system/bin/mediaserverclass mainuser mediagroup audio camera inet net_bt net_bt_admin net_bw_acct drmrpc mediadrm qcom_diagioprio rt 4
In the text message app, you can disable automatic download of MMS by setting (this option can be found on Xiaomi, Huawei, and other mobile phones) to reduce risks.
However, this cannot prevent malicious videos such as sdcard root directories, download directories, and bluetooth directories from being sent through various channels (automatic browser downloads, usb copies, bluetooth, etc ), once a user opens a file browsing or image library app, or even directly accesses a video in a browser, the user will be attacked.
So everyone is happy to wait for the patch!