CVE-2017-13253 :Android Drm服务 堆溢出漏洞
2019-09-01 11:22:00 Author: xz.aliyun.com(查看原文) 阅读量:205 收藏

跟着[1]调试CVE-2017-13253,
[1]使用的调试环境为nexus,HAL的实现没有分开,我的调试环境为pixel,HAL的实现分开了,我在这里整理成一篇文章,记录踩过的坑。
CVE-2017-13253 为Android Drm服务中的堆溢出漏洞。

1 DRM

Android Drm 属于 Android Native 多媒体框架中的一部分。在播放受DRM保护的内容 如Google Play电影中的影片时,会使用DRM服务器。该服务器会采用安全方式对加密的数据进行解密,因此可以访问证书和密钥存储以及其他敏感组件。但由于供应商依赖关系,DRM进程尚未应用于所有情况。
DRM 的架构如下图所示:

2 Crypto Plugins

Crypto Plugins 为DRM方案之一,在Android术语中,每个DRM方案的处理程序称为插件。供应商负责提供这些插件,但供应商可以使用AOSP中一些有用的代码。例如,AOSP包含ClearKey DRM方案插件的完整开源实现。
java层 所涉及到的参数变量

https://developer.android.com/reference/android/media/MediaCodec.CryptoInfo.html#numSubSamples
Binder层的解释 这里引用原文的图

MediaDrmserver 提供了一个到Crypto对象。名字叫做ICrypto,现在改名叫CryptoHal。此接口的一般用途是允许非特权应用解密DRM数据,这需要更高的解密权限,例如访问TEE。
ICrypto里有很多种方法,毫无疑问最重要的方法是解密:

3 Binder's C++库

ICrypto应该是一个接口类,Binder’s C++库里提供了许多依赖于Binder的C++代码的抽象。可以调用C++类的远程实例方法。使用此机制的每个对象在预定义的结构中实现几个类:

*  接口类,定义应该通过Binder调用的对象的方法,以“I”为前缀
* ”客户端“类,负责序列化输入和反序列化输出,以”Bp“为前缀
* ”服务器端“类,负责反序列化输入和序列化输出,以”Bn“为前缀

最终 在使用对象时,几乎总是使用接口类型。这里引用原文的图

这里相关的结构成员是mHeapSeqNum和两个mSharedMemory成员(DestinationBuffer的其余部分是在目标未存储为共享内存的情况下,与此漏洞无关的情况)。这里使用名称堆来指代实际的共享内存。 mHeapSeqNum是这样的内存的标识符,之前使用ICrypto的方法(称为setHeap)共享。两个mSharedMemory成员仅表示堆内缓冲区的偏移和大小。这意味着尽管mHeapSeqNum位于源结构中,但它实际上与两者相关。
值得注意的是,参数结构的某些部分有点奇怪。 mSharedMemory是一个IMemory,它实际上连接到自己的堆,并且应该在其中表示一个缓冲区,但是这个堆被忽略,偏移量和大小用于mHeapSeqNum堆。源结构中也存在mHeapSeqNum,但它与源和目标都有关。这是最近对此代码进行更改的结果,该代码是作为Android框架的一个名为Project Treble的主要重新架构师的一部分而制作

4 源码

source.mSharedMemory 是什么含义呢 ,我猜是图里面的这个 shared memory?仅表示堆内缓冲区的偏移和大小,实际上连接到自己的堆,应该用于表示一个缓冲区,但是这个堆被忽略,偏移量和大小用于mHeapSeqNum堆。
在ICrypto interface 的“服务端”代码,用于验证共享内存缓冲区的subsample。这段代码检查
subsample Encrypt Clear data都加起来 sum<= SIZE_MAX
subsampleSizes == totalSize
totalSize <= source.mSharedMemory->size()
Offset <= source.mSharedMemory->size() - totalSize

http://androidxref.com/8.0.0_r4/xref/frameworks/av/drm/libmediadrm/ICrypto.cpp#370
接着进入 decrypt,对数据流进行序列化并继续进行验证

http://androidxref.com/8.0.0_r4/xref/hardware/interfaces/drm/1.0/default/CryptoPlugin.cpp#111
sourceBase 指向 mSharedBuffer上的 source
'''
111 if (source.offset + offset + source.size > sourceBase->getSize()) {
112 _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, 0, "invalid buffer size");
113 return Void();
114 }
'''
此处的本意是拷贝 的size = offset+所有的 subSamples 。

'''
uint8_t *base = static_cast<uint8_t *>(static_cast<void *>(sourceBase->getPointer()));
Const SHaredBuufer& destBuffer = destination.nonsecureMemory;
Sp<IMemory> destBase = mSharedBufferMap[destBuffer.bufferId]
验证 
source.offset + offset + source.size > sourceBase->getSize()
source.offset 是source 在heap中的相对偏移
destBuffer.offset+destBuffer.size>destBase->getSize()
'''

第一处检查是offsets和buffer size没有超出堆的size。SourceBase是堆,而source现在是source.mSharedMemory。
此处的本意是检查偏移+需要拷贝的数据是否有超出这段sharedMemory的size??

另一处检查类似,但是是在destBuffer上执行。destBuffer 是 destination.mSharedMemory、destBase “same heap as”sourceBase。也就是说 destBuffer 和 sourceBuffer 都在同一段SharedMemory上。

最终,每个buffer简化为一个指向内存的指针,偏移现在是指针的一部分。
最后一处代码


http://androidxref.com/8.0.0_r4/xref/frameworks/av/drm/mediadrm/plugins/clearkey/CryptoPlugin.cpp#45
当 满足mode == kMode_Unencrypted时,
会执行到
'''
memcpy(reinterpret_cast<uint8_t>(dstPtr) + offset,
reinterpret_cast<const uint8_t
>(strPtr) + offset,
subSample.mNumBytesOfClearData);
'''

4 漏洞处

漏洞原因:没有检查 要复制的数据+目标缓冲区的位置是否超过 堆的 size,属于检查不完整。

只有一个简单的检查 源缓冲区 ,BnCrypto的第三次检查了这一点,下一处的检查 考虑了源缓冲区+offset。唯一检查和目标缓冲区有关的是 第二处的检查,但是太简单不足以阻止这种问题。 第二处的检查,确认目标缓冲区在堆内,且没有超出缓冲区的边界。

动态调试[6][7][8]

在poc作者的github[9]里说到运行的结果应该是

  1. 如果在2018年3月之后的Android版本 decrypt 会返回bad_value(-22)。
  2. 如果没有crash(overwritten data 是可写的)decrypt return 他copy的数据的量。
  3. 如果供应商将HAL实现为单独的进程?(例如Pixel 2),则解密应该返回UNKNOWN_ERROR(-32)。
  4. 如果供应商在同一过程中实施HAL?(例如Nexus 5X),则解密应返回0。

HAL的实现会影响是否造成Crash。此处没有返回UNKNOWN_ERROR

本漏洞涉及三处函数,分别是
android::BnCrypto::onTransact 序列化+验证
Android::CryptoHal::decrypt 序列化+验证
Clearkeydrm::CryptoPlugin::decrypt memcpy

前面两次进入BnCrypto::onTransact函数是调用createPlugin和setHeap,第三次调用decrypt
对android::BnCrypto::onTransact 、android::CryptoHal::decrypt的调试部分可参考[1]调试。

结果如下图所示:

从反序列化种获取total值。这里能看到是0x2000

在对Clearkeydrm::CryptoPlugin::decrypt 实际调试时,供应商在实现的时候,对HAL分开实现。所以此处为多进程调试。
此处有两种方法找到binder的server端,如图所示
1.通过Android.mk or Android.bp文件 找到local_module

2.通过adb shell ps | grep 'xxxserver'

最后 找到漏洞点

1

受影响的流程取决于供应商如何实现。如果供应商未将HAL分成不同的进程,则mediadrmserver受影响。如果供应商讲HAL分开,那么使用默认加密插件的Crypto插件的每个HAL服务都会收到影响。由于默认的Crypto插件代码只留下指向目标缓冲区的指针,并且大小仅由子样本确定,因此供应商代码无法告知它收到格式错误的数据。

2

对这个漏洞的补丁很简单,增加对要传递数据检查的完整性, dest->size() 目标缓冲区堆的size,检查目标缓冲区的偏移+要拷贝的数据是否超出目标缓冲区堆的总和。

[1] https://bbs.pediy.com/thread-225398.htm
[2]http://wangkuiwu.github.io/page2/ Android Binder机制
[3]https://www.anquanke.com/vul/id/1124887 漏洞简介
[4]https://source.android.com/devices/drm DRM,安卓版权管理框架
[5]https://developer.android.com/reference/android/media/MediaDrm.html MediaDrm
[6]https://source.android.com/setup/build/building 编译流程
[7]https://developers.google.com/android/drivers
[8]https://source.android.com/setup/start/build-numbers#source-code-tags-and-builds
[9]https://github.com/tamirzb/CVE-2017-13253


文章来源: http://xz.aliyun.com/t/6144
如有侵权请联系:admin#unsafe.sh