<pre id="vvttv"><mark id="vvttv"><progress id="vvttv"></progress></mark></pre>
    <pre id="vvttv"></pre>

      <p id="vvttv"></p>

          <p id="vvttv"></p>

                <p id="vvttv"></p>

                <pre id="vvttv"><cite id="vvttv"><progress id="vvttv"></progress></cite></pre>

                  <output id="vvttv"><dfn id="vvttv"><th id="vvttv"></th></dfn></output>

                    <p id="vvttv"></p>

                    原文地址:http://drops.wooyun.org/papers/7506

                    0x00 前言


                    文章對應著CVE-2015-{1538,1539,3824,3826,3827,3828,3829}7個CVE,具體映射關系目前不明。此次安全漏洞號稱影響“95%”安卓手機的安全。通過跟進此次漏洞的攻擊面來看,這種說法毫不夸張,外界報道的關于一個彩信就直接打下機器的說法也是可信的。但這也僅僅是眾多攻擊面中的一條而已。

                    0x01 攻擊面分析


                    libStagefright默認會被mediaserver使用,也就是說,如果惡意的視頻文件有機會被mediaserver處理到,該漏洞就有機會觸發,舉例:

                    如文件管理app,如果視頻被存放在sdcard,那么打開文件管理app,下拉列表到露出視頻,就會觸發縮略圖解析,漏洞觸發。

                    圖庫app,點擊本地圖片會出現縮略圖,如果視頻在sdcard,或者download目錄,這時候也會觸發。

                    微信同樣受到影響。通過微信發送的視頻,點擊也會導致media server崩潰。此外,收到的視頻即使用戶不點擊,后面在微信中發送圖片時,也會造成前面gallery,文件管理器同樣的效果,也會觸發縮略圖過程并溢出。

                    enter image description here

                    在最新版的Chrome43版中打開一個video鏈接(mp4),無需點擊自動觸發。

                    enter image description here

                    開機同樣是一個觸發點,mediaprovider會掃描sd卡里的所有文件,并且嘗試去解析,恩開機自啟動

                    enter image description here

                    media framework的架構如下:基本上采用了android的media框架來開發的程序都會受到影響。

                    enter image description here

                    看到這里,想說的是,外界所謂的那些,關閉彩信功能保平安也就尋求個心理安慰吧。從根源上看大部分(有一個例外)都和整數計算的上溢/下溢相關,因為這個問題,間接導致了后續的內存破壞等相關的安全問題。

                    1.1. 代碼分析

                    1.1.1. No1 heap 讀越界

                    #!c
                    1. status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) {
                    2. uint32_t hdr[2];
                    3. uint64_t chunk_size = ntohl(hdr[0]);
                    4. uint32_t chunk_type = ntohl(hdr[1]);
                    5.
                    6. switch(chunk_type) {
                    

                    只有下面幾種chunk_type才會觸發分支parse3GPPMetaData:

                    #!c
                    1. case FOURCC('t', 'i', 't', 'l'):
                    2. case FOURCC('p', 'e', 'r', 'f'):
                    3. case FOURCC('a', 'u', 't', 'h'):
                    4. case FOURCC('g', 'n', 'r', 'e'):
                    5. case FOURCC('a', 'l', 'b', 'm'):
                    6. case FOURCC('y', 'r', 'r', 'c'):
                    7. {
                    8. *offset += chunk_size;
                    9.
                    10. status_t err = <span style="color: #ff0000;">parse3GPPMetaData</span>(data_offset, chunk_data_size, depth);
                    11.
                    12. if (err != OK) {
                    13. return err;
                    14. }
                    15.
                    16. break;
                    17. }
                    

                    以上parse3GPPMetaData會觸發兩個3gp格式的漏洞。 第一個setCString heap讀越界,首先從文件中offset讀size數據到buffer。

                    #!c
                    status_t MPEG4Extractor::parse3GPPMetaData(off64_t offset, size_t size, int depth) {
                    1. if (size &lt; 4) {
                    2. return ERROR_MALFORMED;
                    3. }
                    4.
                    5. uint8_t *buffer = new (std::nothrow) uint8_t[size];
                    6. if (buffer == NULL) {
                    7. return ERROR_MALFORMED;
                    8. }
                    9. <span style="color: #ff0000;">if (mDataSource-&gt;readAt(</span>
                    10. <span style="color: #ff0000;">offset, buffer, size) != (ssize_t)size) {</span>
                    11. delete[] buffer;
                    12. buffer = NULL;
                    13.
                    14. return ERROR_IO;
                    15. }
                    

                    然后,這個類似strcpy,所以就是 mFileMetaData->setCString(metadataKey, (const char *)buffer + 6);

                    https://android.googlesource.com/platform/frameworks/av/+/android-5.1.1_r8/media/libstagefright/MetaData.cpp

                    #!c
                    1. bool MetaData::setCString(uint32_t key, const char *value) {
                    2. return setData(key, TYPE_C_STRING, value, <span style="color: #ff0000;">strlen</span>(value) + 1);
                    3. }
                    
                    1. bool MetaData::setData(
                    2. uint32_t key, uint32_t type, const void *data, size_t size) {
                    3. bool overwrote_existing = true;
                    4.
                    5. ssize_t i = mItems.indexOfKey(key);
                    6. if (i &lt; 0) {
                    7. typed_data item;
                    8. i = mItems.add(key, item);
                    9.
                    10. overwrote_existing = false;
                    11. }
                    12.
                    13. typed_data &amp;item = mItems.editValueAt(i);
                    14.
                    15. item.setData(type, data, size);
                    16.
                    17. return overwrote_existing;
                    18. }
                    

                    注意到size是動態的,所以這里一般不會溢出,但會出現讀越界。

                    #!c
                    1. void MetaData::typed_data::setData(
                    2. uint32_t type, const void *data, size_t size) {
                    3. clear();
                    4.
                    5. mType = type;
                    6. <span style="color: #ff0000;">allocateStorage</span>(size);
                    7. memcpy(storage(), data, size);
                    8. }
                    

                    讀到的內容被保存到一個metadata中,或許可以泄漏(例如title, artist這些信息)

                    enter image description here

                    1.1.2. No2 heap 越界寫

                    第二個是under flow,如果size<6,那么len16會很大,會對buffer(還是剛才的heap)后面很大一片內存作bswap_16操作,寫的內容不太可控

                    #!c
                    1. if (metadataKey &gt; 0) {
                    2. bool isUTF8 = true; // Common case
                    3. char16_t *framedata = NULL;
                    4. int len16 = 0; // Number of UTF-16 characters
                    5.
                    6. // smallest possible valid UTF-16 string w BOM: 0xfe 0xff 0x00 0x00
                    7. if (size - 6 &gt;= 4) {
                    8. len16 = ((size - 6) / 2) - 1; // don't include 0x0000 terminator
                    9. framedata = (char16_t *)(buffer + 6);
                    10. if (0xfffe == *framedata) {
                    11. // endianness marker (BOM) doesn't match host endianness
                    12. for (int i = 0; i &lt; len16; i++) {
                    13. framedata[i] = <span style="color: #ff0000;">bswap_16</span>(framedata[i]);
                    14. }
                    15. // BOM is now swapped to 0xfeff, we will execute next block too
                    }
                    

                    根據前面的計算,這里的size就是chunk_data_size,代表這個chunk中除去header外的data size。計算方式如下:

                    off64_t data_offset = *offset + 8;在解析header過程中自然標記data開始的offset

                    #!c
                    off64_t chunk_data_size = *offset + chunk_size – data_offset;
                    

                    所以chunk_size<14>8即可。Chunk_size來自文件tag前面4字節。

                    1.1.3. No3 heap overflow

                    然后是mpeg tx3g tag的,chunk_sizeuint,與size之和溢出,導致實際分配比size小的內存。后面的memcpy heap overflow,寫入的data應該是可控的。

                    enter image description here

                    enter image description here

                    將trak修改為tx3g,然后前面的改為ffff

                    enter image description here

                    1.1.4. No4 heap 越界讀

                    出現在covr這個tag處理時,chunk_data_size小于kSkipBytesOfDataBox時,setData會讀過buffer的邊界。由于setData會分配內存,但多半分配失敗,所以可能也會導致向地址為0的內存寫入。

                    enter image description here

                    1.1.5. No5 heap 越界寫

                    chunk_data_size=SIZE_MAX時,+1導致分配0長度的內存,后面的readAt會邊讀文件邊寫入buffer,在讀到文件結束之前已經導致了heap write 越界。由于覆蓋數據來自文件,所以內容與長度都是可控的。

                    enter image description here

                    1.1.6. No6 Integer overflow

                    處理stsc這種tag時,調用了setSampleToChunkParams方法

                    #!c
                    1. case FOURCC('s', 't', 's', 'c'):
                    2. {
                    3. status_t err =
                    4. mLastTrack-&gt;sampleTable-&gt;setSampleToChunkParams(
                    5. data_offset, chunk_data_size);
                    6.
                    7. *offset += chunk_size;
                    8.
                    9. if (err != OK) {
                    10. return err;
                    11. }
                    12.
                    13. break;
                    14. }
                    

                    這個方法內有integer overflow,主要是循環過程中,在計算類似mSampleToChunkEntries[i].startChunk的時候,內部實際上是按照i*sizeof(SampleToChunkEntry)+ offset(startChunk)來計算的,這里可能overflow,但從這里看不一定造成內存破壞,可能會干擾執行邏輯。

                    https://android.googlesource.com/platform/frameworks/av/+/android-5.1.1_r8/media/libstagefright/SampleTable.cpp

                    #!c
                    1. mSampleToChunkEntries =
                    2. new SampleToChunkEntry[mNumSampleToChunkOffsets];
                    3.
                    4. for (uint32_t i = 0; i &lt; mNumSampleToChunkOffsets; ++i) {
                    5. uint8_t buffer[12];
                    6. if (mDataSource-&gt;readAt(
                    7. mSampleToChunkOffset + 8 + i * 12, buffer, sizeof(buffer))
                    8. != (ssize_t)sizeof(buffer)) {
                    9. return ERROR_IO;
                    10. }
                    11.
                    12. CHECK(U32_AT(buffer) &gt;= 1); // chunk index is 1 based in the spec.
                    13.
                    14. // We want the chunk index to be 0-based.
                    15. mSampleToChunkEntries[i].startChunk = U32_AT(buffer) - 1;
                    16. mSampleToChunkEntries[i].samplesPerChunk = U32_AT(&amp;buffer[4]);
                    17. mSampleToChunkEntries[i].chunkDesc = U32_AT(&amp;buffer[8]);
                    18. }
                    

                    所以補丁增加了校驗

                    #!c
                    + if (SIZE_MAX / sizeof(SampleToChunkEntry) &lt;= mNumSampleToChunkOffsets)
                    + return ERROR_OUT_OF_RANGE;
                    

                    1.1.7. No7 parseESDescriptor Integer overflow

                    這里的主要問題是只在開始檢查了size>=3,然后就-2,–,后面又繼續幾次-2,-length都沒法保證不溢出。

                    #!c
                    1. status_t ESDS::parseESDescriptor(size_t offset, size_t size) {
                    2. if (size &lt; 3) {
                    3. return ERROR_MALFORMED;
                    4. }
                    5.
                    6. offset += 2; // skip ES_ID
                    7. size -= 2;
                    8.
                    9. unsigned streamDependenceFlag = mData[offset] &amp; 0x80;
                    10. unsigned URL_Flag = mData[offset] &amp; 0x40;
                    11. unsigned OCRstreamFlag = mData[offset] &amp; 0x20;
                    12.
                    13. ++offset;
                    14. --size;
                    15.
                    16. if (streamDependenceFlag) {
                    17. offset += 2;
                    18. <span style="color: #ff0000;">size -= 2;</span>
                    19. }
                    20.
                    21. if (URL_Flag) {
                    22. if (offset &gt;= size) {
                    23. return ERROR_MALFORMED;
                    24. }
                    25. unsigned URLlength = mData[offset];
                    26. offset += URLlength + 1;
                    27. <span style="color: #ff0000;">size -= URLlength + 1;</span>
                    28. }
                    29.
                    30. if (OCRstreamFlag) {
                    31. offset += 2;
                    32.<span style="color: #ff0000;"> size -= 2;</span>
                    33.
                    34. if ((offset &gt;= size || mData[offset] != kTag_DecoderConfigDescriptor)
                    35. &amp;&amp; offset - 2 &lt; size
                    36. &amp;&amp; mData[offset - 2] == kTag_DecoderConfigDescriptor) {
                    37. // Content found "in the wild" had OCRstreamFlag set but was
                    38. // missing OCR_ES_Id, the decoder config descriptor immediately
                    39. // followed instead.
                    40. offset -= 2;
                    41. size += 2;
                    42.
                    43. ALOGW("Found malformed 'esds' atom, ignoring missing OCR_ES_Id.");
                    44. }
                    45. }
                    46.
                    47. if (offset &gt;= size) {
                    48. return ERROR_MALFORMED;
                    49. }
                    50.
                    51. uint8_t tag;
                    52. size_t sub_offset, sub_size;
                    53. status_t err = <span style="color: #ff0000;">skipDescriptorHeader</span>(
                    54. offset, size, &amp;tag, &amp;sub_offset, &amp;sub_size);
                    

                    雖然沒直接看到溢出的size造成影響,但可能會造成開發者未預料到的邏輯。

                    #!c
                    1. do {
                    2. if (size == 0) {
                    3. return ERROR_MALFORMED;
                    4. }
                    5.
                    6. uint8_t x = mData[offset++];
                    7. --size;
                    8.
                    9. *data_size = (*data_size &lt;&lt; 7) | (x &amp; 0x7f);
                    10. more = (x &amp; 0x80) != 0;
                    11. }
                    12. while (more);
                    

                    1.1.8. No8 SampleTable Integer overflow

                    https://android.googlesource.com/platform/frameworks/av/+/android-5.1.1_r8/media/libstagefright/SampleTable.cpp

                    32位uint相乘,然后將結果轉化為64位uint

                    #!c
                    1.  uint32_t mTimeToSampleCount;
                    2.  mTimeToSampleCount = U32_AT(&header[4][4]);
                    3.  uint64\_t allocSize = mTimeToSampleCount * 2 * sizeof(uint32\_t);
                    4.  if (allocSize > SIZE_MAX) {
                    5.  return ERROR\_OUT\_OF_RANGE;
                    6.  }
                    7. 
                    

                    這里存在溢出問題,雖然未看到直接的影響,但可能造成后面的檢查誤判。修復方法如下:

                    #!c
                    - uint64_t allocSize = mTimeToSampleCount * 2 * sizeof(uint32_t);
                    + uint64_t allocSize = mTimeToSampleCount * 2 * (uint64_t)sizeof(uint32_t);
                    

                    1.2. 總結

                    前面1-8個漏洞有相似之處。

                    No1 : 一段數據被計算strlen,然后分配內存并strcpy,但這段數據并非一定以’\0’結束,所以導致讀越界

                    No2-5:都是tag前的4字節size沒有校驗,可以任意取值,導致一系列的size計算問題。如下圖所示,所有tag4字節前面都有4字節的size

                    No6-8:都是integer overflow,但沒有看到直接內存破壞的錯誤。可能會造成數據異常等。

                    enter image description here

                    1.3. POC

                    1到5這5個漏洞的觸發路徑非常明了。都是在parseChunk遇到某種特殊tag時,分支處理邏輯出現問題。所以構造poc只需要修改對應的tag即可。

                    特別是2-4,都是tag前的4字節size出現問題。只需要調整對應的size。

                    以下的POC是針對no3,將其中一個trak tag修改為tx3g,然后將前面的size修改為4個FF

                    #!c
                    07-28 20:16:10.888: I/DEBUG(247): *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
                    07-28 20:16:10.888: I/DEBUG(247): Build fingerprint: 'Xiaomi/cancro/cancro:4.4.4/KTU84P/4.8.22:user/release-keys'
                    07-28 20:16:10.888: I/DEBUG(247): Revision: '0'
                    <span style="color: #ff0000;">07-28 20:16:10.888: I/DEBUG(247): pid: 10928, tid: 10945, name: Binder_4 &gt;&gt;&gt; /system/bin/mediaserver &lt;&lt;&lt;
                    07-28 20:16:10.888: I/DEBUG(247): signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 00000004</span>
                    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 000000a9 fp 00000000
                    07-28 20:16:10.978: I/DEBUG(247): ip b66267b7 sp b187a690 lr b6df08df pc b664346e 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 3f77ff86776369e9 d25 bf77ff86919d591e
                    07-28 20:16:10.978: I/DEBUG(247): d26 3fe0000000000000 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:
                    07-28 20:16:10.978: I/DEBUG(247): <span style="color: #ff0000;">#00 pc 0006846e /system/lib/libstagefright.so   
                    (android::MPEG4Extractor::parseChunk(long long*, int)+4345)</span>
                    07-28 20:16:10.978: I/DEBUG(247): #01 pc 000675fb /system/lib/libstagefright.so (android::MPEG4Extractor::parseChunk(long 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)
                    07-28 20:16:10.978: I/DEBUG(247): #04 pc 00092077 /system/lib/libstagefright.so 
                    (android::ExtendedUtils::MediaExtractor_CreateIfNeeded(android::sp&lt;android::MediaExtractor&gt;, android::sp&lt;android::DataSource&gt; const&amp;, char const*)+206)
                    07-28 20:16:10.978: I/DEBUG(247): #05 pc 00075a43 /system/lib/libstagefright.so 
                    (android::MediaExtractor::Create(android::sp&lt;android::DataSource&gt; const&amp;, char const*)+566)
                    07-28 20:16:10.978: I/DEBUG(247): #06 pc 0005a00b /system/lib/libstagefright.so 
                    (android::AwesomePlayer::setDataSource_l(android::sp&lt;android::DataSource&gt; const&amp;)+10)
                    07-28 20:16:10.978: I/DEBUG(247): #07 pc 0005b519 /system/lib/libstagefright.so (android::AwesomePlayer::setDataSource(int, long long, long long)+136)
                    07-28 20:16:10.978: I/DEBUG(247): #08 pc 00034319 /system/lib/libmediaplayerservice.so (android::MediaPlayerService::Client::setDataSource(int, long long, long long)+196)
                    07-28 20:16:10.978: I/DEBUG(247): <span style="color: #ff0000;">#09 pc 00059b2d /system/lib/libmedia.so (android::BnMediaPlayer::onTransact(unsigned int, android::Parcel const&amp;, android::Parcel*, unsigned int)+332)</span>
                    07-28 20:16:10.978: I/DEBUG(247):<span style="color: #ff0000;"> #10 pc 00019225 /system/lib/libbinder.so (android::BBinder::transact(unsigned int, android::Parcel const&amp;, android::Parcel*, unsigned int)+60)</span>
                    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
                    07-28 20:16:10.978: I/DEBUG(247): #15 pc 0000ea5d /system/lib/libutils.so (android::Thread::_threadLoop(void*)+216)
                    07-28 20:16:10.978: I/DEBUG(247): #16 pc 0000e58f /system/lib/libutils.so
                    07-28 20:16:10.978: I/DEBUG(247): #17 pc 0000d248 /system/lib/libc.so (__thread_entry+72)
                    07-28 20:16:10.978: I/DEBUG(247): #18 pc 0000d3e0 /system/lib/libc.so (pthread_create+240)
                    

                    trace看出,這里是通過binder來調用media server提供的接口,進而對視頻處理解析過程崩潰。所以溢出在media server進程。

                    1.4. 防護

                    由于media是安卓中非常核心的一個服務(雖然權限不高),大量的功能都涉及到這個服務。如果僅僅stop media來停止這個服務,手機基本無法使用,例如無法顯示出桌面。

                    service media /system/bin/mediaserver class main user media group audio camera inet net_bt net_bt_admin net_bw_acct drmrpc mediadrm qcom_diag ioprio rt 4

                    在短信app中,通過設置(可在小米,華為等手機中找到這個選項)可以關閉彩信自動下載,降低風險。

                    但這樣無法防止例如sdcard根目錄, 下載目錄, bluetooth這些目錄被通過各種渠道發送過來的惡意視頻(瀏覽器自動下載,usb拷貝,bluetooth,微信等),當用戶一旦打開文件瀏覽或圖庫app,甚至在瀏覽器中直接訪問視頻也會被攻擊。

                    所以大家開心的等補丁吧!

                      <pre id="vvttv"><mark id="vvttv"><progress id="vvttv"></progress></mark></pre>
                      <pre id="vvttv"></pre>

                        <p id="vvttv"></p>

                            <p id="vvttv"></p>

                                  <p id="vvttv"></p>

                                  <pre id="vvttv"><cite id="vvttv"><progress id="vvttv"></progress></cite></pre>

                                    <output id="vvttv"><dfn id="vvttv"><th id="vvttv"></th></dfn></output>

                                      <p id="vvttv"></p>

                                      这里只有精品视频