CVE漏洞中文网

0DayBank一个专门收集整理全球互联网漏洞的公开发布网站
  1. 首页
  2. 百科
  3. 正文

stagefright

2017年5月23日 797点热度 0人点赞 0条评论

stagefright

  • 首页
  • 分类阅读
  • 文库
  • 专栏
  • 公开课
  • 商城
  • 漏洞盒子
注册 | 登录
投稿

技术分析:关于安卓libStagefright系列漏洞分析

百度云安全2015-07-30共200537人围观 ,发现 6 个不明物体漏洞

文章对应着CVE-2015-{1538,1539,3824,3826,3827,3828,3829}7个CVE,具体映射关系目前不明。此次安全漏洞号称影响“95%”安卓手机的安全。通过跟进此次漏洞的攻击面来看,这种说法毫不夸张,外界报道的关于一个彩信就直接打下机器的说法也是可信的。但这也仅仅是众多攻击面中的一条而已。

攻击面分析

libStagefright默认会被mediaserver使用,也就是说,如果恶意的视频文件有机会被mediaserver处理到,该漏洞就有机会触发,举例:

如文件管理app,如果视频被存放在sdcard,那么打开文件管理app,下拉列表到露出视频,就会触发缩略图解析,漏洞触发图库app,点击本地图片会出现缩略图,如果视频在sdcard,或者download目录,这时候也会触发。

微信同样受到影响。通过微信发送的视频,点击也会导致media server崩溃。此外,收到的视频即使用户不点击,后面在微信中发送图片时,也会造成前面gallery,文件管理器同样的效果,也会触发缩略图过程并溢出。

在最新版的Chrome43版中打开一个video链接(mp4),无需点击自动触发

开机同样是一个触发点,mediaprovider会扫描sd卡里的所有文件,并且尝试去解析,恩开机自启动

media framework的架构如下:基本上采用了android的media框架来开发的程序都会受到影响。

看到这里,想说的是,外界所谓的那些,关闭彩信功能保平安也就寻求个心理安慰吧。从根源上看大部分(有一个例外)都和整数计算的上溢/下溢相关,因为这个问题,间接导致了后续的内存破坏等相关的安全问题。

代码分析

1.1.1. No1 heap 读越界

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:

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。

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

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是动态的,所以这里一般不会溢出,但会出现读越界。

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这些信息)

1.1.2. No2 heap 越界写

第二个是under flow,如果size<6,那么len16会很大,会对buffer(还是刚才的heap)后面很大一片内存作bswap_16操作,写的内容不太可控

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
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_size是uint,与size之和溢出,导致实际分配比size小的内存。后面的memcpy heap overflow,写入的data应该是可控的。

1.1.4. No4 heap 越界读

出现在covr这个tag处理时,chunk_data_size小于kSkipBytesOfDataBox时,setData会读过buffer的边界。由于setData会分配内存,但多半分配失败,所以可能也会导致向地址为0的内存写入。

1.1.5. No5 heap 越界写

当chunk_data_size=SIZE_MAX时,+1导致分配0长度的内存,后面的readAt会边读文件边写入buffer,在读到文件结束之前已经导致了heap write 越界。由于覆盖数据来自文件,所以内容与长度都是可控的。

1.1.6. No6 Integer overflow

处理stsc这种tag时,调用了setSampleToChunkParams方法

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

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. }

所以补丁增加了校验

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

1.1.7. No7 parseESDescriptor Integer overflow

这里的主要问题是只在开始检查了size>=3,然后就-2,–,后面又继续几次-2,-length都没法保证不溢出。

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造成影响,但可能会造成开发者未预料到的逻辑。

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

1. uint32_t mTimeToSampleCount;
2. mTimeToSampleCount = U32_AT(&amp;header[4]);
3. uint64_t allocSize = mTimeToSampleCount * 2 * sizeof(uint32_t);
4. if (allocSize &gt; SIZE_MAX) {
5. return ERROR_OUT_OF_RANGE;
6. }

这里存在溢出问题,虽然未看到直接的影响,但可能造成后面的检查误判。修复方法如下:

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

总结

前面1-8个漏洞有相似之处。

No1 : 一段数据被计算strlen,然后分配内存并strcpy,但这段数据并非一定以’\0’结束,所以导致读越界

No2-5:都是tag前的4字节size没有校验,可以任意取值,导致一系列的size计算问题。如下图所示,所有tag4字节前面都有4字节的size:

No6-8:都是integer overflow,但没有看到直接内存破坏的错误。可能会造成数据异常等。

POC

1到5这5个漏洞的触发路径非常明了。都是在parseChunk遇到某种特殊tag时,分支处理逻辑出现问题。所以构造poc只需要修改对应的tag即可。

特别是2-4,都是tag前的4字节size出现问题。只需要调整对应的size。

以下的POC是针对no3,将其中一个trak tag修改为tx3g,然后将前面的size修改为4个FF

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进程。

防护

由于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,甚至在浏览器中直接访问视频也会被攻击。

所以大家开心的等补丁吧!

*作者:百度安全实验室,转载请注明来自FreeBuf黑客与极客(FreeBuf.COM)

百度云安全

百度云安全13 篇文章等级: 4级
|
|
  • 上一篇:苹果APP Store和iTunes Store曝高危漏洞(含视频)
  • 下一篇:安卓再曝新漏洞:可远程致使用户无法接打电话

这些评论亮了

  • 路人甲回复
    只有一种办法修复这个漏洞:利用它来推送补丁。
    )7(亮了
发表评论

已有 6 条评论

  • 熊猫阿B0 2015-07-30回复1楼

    那我用客户端看 呢?

    亮了(3)
  • jduck 2015-07-30回复2楼

    某厂的初探在哪里~ 2333333

    亮了(2)
  • 路人甲 2015-07-30回复3楼

    只有一种办法修复这个漏洞:利用它来推送补丁。

    亮了(7)
  • 牙僧 2015-07-30回复4楼

    cc

    亮了(3)
  • 义水黄沙 2015-07-30回复5楼

    坐等厂商补丁!

    亮了(1)
  • Anonymous 2015-07-30回复6楼

    :evil:

    亮了(2)

必须您当前尚未登录。登陆?注册

必须(保密)

表情插图

取消

百度云安全

百度云安全

百度云安全部官方

13 篇文章0 条评论

相关阅读

  • 域安全篇:寻找SYSVOL里的密码和攻击GPP(组策略偏好)
  • 利用HSTS嗅探浏览器历史纪录的三个漏洞
  • QEMU新虚拟机逃逸漏洞深入分析
  • 社交网站LinkedIn(领英)修复帮助中心处的XSS
  • 霍尼韦尔Midas气体检测器被曝严重漏洞,再为工控安全敲响警钟

特别推荐

关注我们 分享每日精选文章

不容错过

  • 专注“钓鱼人防”:安全新星企业PhishMe浅析kuma2017-04-19
  • 获取乘客和车主的个人信息?这记Uber逻辑漏洞“组合拳”值得一看ArthurKiller2016-06-28
  • 【企业研究报告】FreeBuf Insight:网络安全创新企业Top 10解读之root9BFreeBuf研究院2016-11-30
  • 检测一下你的专业指数:2015年十大测试工具你认识几个?FireFrank2015-12-29

FREEBUF

  • 免责声明
  • 关于我们
  • 加入我们

广告及服务

  • 寻求报道
  • 广告合作
  • 联系我们
  • 友情链接

关注我们

  • 官方微信
  • 新浪微博
  • 腾讯微博
  • Twitter

赞助商

Copyright © 2013 WWW.FREEBUF.COM All Rights Reserved 沪ICP备13033796号

css.php

正在加载中...

0daybank

标签: 暂无
最后更新:2017年5月23日

小助手

这个人很懒,什么都没留下

点赞
< 上一篇
下一篇 >

文章评论

您需要 登录 之后才可以评论

COPYRIGHT © 2024 www.pdr.cn CVE漏洞中文网. ALL RIGHTS RESERVED.

鲁ICP备2022031030号

联系邮箱:wpbgssyubnmsxxxkkk@proton.me