本站总访问量
文章目录
  1. 1. StageFright漏洞有多犀利
  2. 2. 什么是StageFright
  3. 3. 漏洞简介
  4. 4. 漏洞影响
  5. 5. MPEG4文件格式基础
  6. 6. 两枚MPEG4_covr漏洞分析
    1. 6.1. 第一个漏洞
    2. 6.2. 第二个漏洞
  7. 7. 攻击路径
  8. 8. 解决方案建议

扣吧力作,欢迎转载,转载请注明来自colbert337.github.io

StageFright漏洞有多犀利

yitiaocaixin

什么是StageFright

StageFright是一个Android中的系统服务,会被mediaserver调用,可处理各种多媒体格式,由Natvie C++代码实现:

stragefright

漏洞简介

Android Stagefright框架中发现了多个integer overflow/underflow等漏洞,可导致拒绝服务,甚至有任意代码执行等问题。

攻击者通过发送包含特制媒体文件的MMS或WEB页来触发该漏洞。由于stagefright不只是用来播放媒体文件的,还能自动产生缩略图,或者从视频或音频文件中抽取元数据,如长度、高度、宽度、帧频、频道和其他类似信息。因此接收到恶意彩信的用户只要查看缩略图就可触发该漏洞。

漏洞影响

“Stagefright”媒体播放引擎库在Android 2.2中引入,至5.1的所有版本上均存在此漏洞,预计会有95%的Android设备,约有九亿五千万的安卓设备受该漏洞影响.

使用Stagefright库的应用程序以Media权限运行,成功利用漏洞,允许攻击者浏览器媒体库相应的文件,但通过权限提升攻击,可完全控制设备。

该Stagefright漏洞所对应的CVE ID如下:

CVE-2015-1538
CVE-2015-1539
CVE-2015-3824
CVE-2015-3826
CVE-2015-3827
CVE-2015-3828
CVE-2015-3829

MPEG4文件格式基础

这里需要大家简单了解一下MPEG4文件格式。

MP4(MPEG-4 Part 14)是一种常见的多媒体容器格式,它是在“ISO/IEC 14496-14”标准文件中定义的。

MP4是一种描述较为全面的容器格式,被认为可以在其中嵌入任何形式的数据,各种编码的视频、音频等都不在话下。MP4格式的官方文件后缀名是“.mp4”,还有其他的以mp4为基础进行的扩展或者是缩水版本的格式,包括:M4V, 3GP, F4V等。

基础知识:

1、 MP4文件,由许多Box和FullBox组成。

2、 Box,每个Box由Header和Data组成,基本结构是:

box1

3、 FullBox,是Box的扩展,Box结构的基础上在Header中增加8bits version和24bits flags。

4、 Header很重要,包含了整个Box的长度size和类型type。

当size==0时,代表这是文件中最后一个Box;

当size==1时,意味着Box长度需要更多bits来描述,超过了uint32的最大数值,会用往后偏移8位后的8位uint64来存放大小。(有点拗口,但很重要!)

当type是uuid时,代表Box中的数据是用户自定义扩展类型。

5、 Data,是Box的实际数据,可以是纯数据也可以是更多的子Boxes。

6、 当一个Box的Data中是一系列子Box时,这个Box又可成为Container Box。

如图所示:

box2

两枚MPEG4_covr漏洞分析

Stagefright漏洞有7个,为了方便大家理解,笔者只举两个漏洞作为例子。

这两个漏洞都是出现在对MPEG4文件抽取解析covr box的地方,故笔者将其命名为【MPEG4_covr漏洞】

选取了Android 5.1.0_r1的代码来分析,链接如下:

1
http://androidxref.com/5.1.0_r1/xref/frameworks/av/media/libstagefright/MPEG4Extractor.cpp

来来来,上代码,这段代码里面有两个漏洞,你能看出来吗?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
case FOURCC('c', 'o', 'v', 'r'):
{

*offset += chunk_size;

if (mFileMetaData != NULL) {
ALOGV("chunk_data_size = %lld and data_offset = %lld",
chunk_data_size, data_offset);
sp<ABuffer> buffer = new ABuffer(chunk_data_size + 1);
if (mDataSource->readAt(
data_offset, buffer->data(), chunk_data_size) != (ssize_t)chunk_data_size) {
return ERROR_IO;
}
const int kSkipBytesOfDataBox = 16;
mFileMetaData->setData(
kKeyAlbumArt, MetaData::TYPE_NONE,
buffer->data() + kSkipBytesOfDataBox, chunk_data_size - kSkipBytesOfDataBox);
}

break;
}

第一个漏洞

integer overflow when processing covr MPEG4 atoms

1
2
3
4
5
sp<ABuffer> buffer = new ABuffer(chunk_data_size + 1);
if (mDataSource->readAt(
data_offset, buffer->data(), chunk_data_size) != (ssize_t)chunk_data_size) {
return ERROR_IO;
}

由于未对chunk_data_size的长度进行限制,当chunk_data_size = SIZE_MAX = 0xFFFFFFFF,chunk_data_size + 1就会发生整数溢出,使得chunk_data_size + 1 = 0,造成了buffer申请内存的长度为0,然后readAt函数中再对buffer进行拷贝数据操作,导致堆越界。

看看readAt函数的实现,发现直接就用memcpy往*data里面写内容了(也就是buffer->data()),肯定会写穿。

1
2
3
4
5
6
7
8
9
10
11
ssize_t MPEG4DataSource::readAt(off64_t offset, void *data, size_t size) {
Mutex::Autolock autoLock(mLock);

if (offset >= mCachedOffset
&& offset + size <= mCachedOffset + mCachedSize) {
memcpy(data, &mCache[offset - mCachedOffset], size);
return size;
}

return mSource->readAt(offset, data, size);
}

补丁链接:

1
https://github.com/CyanogenMod/android_frameworks_av/commit/c50f5a29f50515c88f1430a1982aea5e19e19b0a

POC也很简单,只需要 chunk_data_size = 0xFFFFFFFF,那么如何做到呢?

仔细阅读MPEG4Extractor.cpp前面的代码:

1
off64_t chunk_data_size = *offset + chunk_size - data_offset;
1
off64_t data_offset = *offset + 8;

可以推导出

1
*offset + chunk_size - (*offset + 8) = 0xFFFFFFFF

也就是

1
chunk_size = 0xFFFFFFFF + 8

我们回顾一下BOX Header的数据结构:

box1

1
2
uint64_t chunk_size = ntohl(hdr[0]);
uint32_t chunk_type = ntohl(hdr[1]);

从代码可以看出:

1) chunk_size其实就是MP4格式的BOX Header中的的Box长度size
2) 这里的chunk_type则是(int)’covr’
3) chunk_size的类型是uint64_t,是可以大于0xFFFFFFFF的:

继续看代码:

1
2
3
4
5
6
if (chunk_size == 1) {
if (mDataSource->readAt(*offset + 8, &chunk_size, 8) < 8) {
return ERROR_IO;
}
chunk_size = ntoh64(chunk_size);
data_offset += 8;

当chunk_size == 1时,就读取*offset + 8处的8个字节,作为chunk_size的值,同时data_offset会加8。(也就是MP4格式基础里面说的,当size==1时,会用往后偏移8位后的8位uint64来存放大小)

由于*offset就是BOX Header的开头,所以我们构造BOX Header的前8位为:

1
00 00 00 01 ‘c’ ‘o’ ‘v’ ‘r’

此时,*offset+8的地方就是真正的chunk_size了,但由于chunk_size == 1时候,data_offset += 8,所以上面的公式已经不成立,需要重新计算:

1
*offset + chunk_size - (*offset + 8 + 8) = 0xFFFFFFFF

最后得出chunk_size = 0xFFFFFFFF + 16 = 0x10000000F

所以最后我们构造BOX Header的为:

1
00 00 00 01 ‘c’ ‘o’ ‘v’ ‘r’ 00 00 00 01 00 00 00 0F

第二个漏洞

integer underflow in covr MPEG4 processing

问题出在这里:

1
2
3
4
const int kSkipBytesOfDataBox = 16;
mFileMetaData->setData(
kKeyAlbumArt, MetaData::TYPE_NONE,
buffer->data() + kSkipBytesOfDataBox, chunk_data_size - kSkipBytesOfDataBox);

看setData的第三个参数,由于未检测chunk_data_size的长度,如果chunk_data_size – kSkipBytesOfDataBox为负数,会导致整数下溢,变成一个非常大的无符号整型。

setData的第三个参数很大会怎么样呢?再看看setData的实现:

1
2
3
4
5
6
7
8
void MetaData::typed_data::setData(
uint32_t type, const void *data, size_t size) {
clear();

mType = type;
allocateStorage(size);
memcpy(storage(), data, size);
}

setData的第三个参数的类型是size_t,当传入size=0xFFFFFFFF,allocateStorage(size)就会失败,返回null,而下一行代码memcpy没有做判断就往storage()写内容了,导致出错。

补丁链接:

1
https://github.com/CyanogenMod/android_frameworks_av/commit/b1f29294f1a5831eb52a81d3ee082a9475f6e879

第二个漏洞的POC就简单多了,因为kSkipBytesOfDataBox = 16,所以推导过程如下:

chunk_data_size <= kSkipBytesOfDataBox
推到出 offset + chunk_size - offset - 8 <= 16
推到出 chunk_size - 8 <= 16
推到出 chunk_size <= 24(0x18)

所以当chunk_size的值<=24时会触发漏洞(chunk_size不能等于1哦)

我们构造BOX Header的一个样本(chunk_size=0x07):

1
00 00 00 07 ‘c’ ‘o’ ‘v’ ‘r’

攻击路径

发送嵌入恶意视频文件的彩信
构建嵌入恶意视频的WEB页,诱使用户打开
在SD卡构建恶意视频文件,诱使用户打开

解决方案建议

尽快打补丁
禁用Stagefright服务
不要轻易打开陌生人发的含有MP4的彩信
留心恶意URL和恶意视频文件

文章目录
  1. 1. StageFright漏洞有多犀利
  2. 2. 什么是StageFright
  3. 3. 漏洞简介
  4. 4. 漏洞影响
  5. 5. MPEG4文件格式基础
  6. 6. 两枚MPEG4_covr漏洞分析
    1. 6.1. 第一个漏洞
    2. 6.2. 第二个漏洞
  7. 7. 攻击路径
  8. 8. 解决方案建议