CVE-2017-13253 Android Drm服务 堆溢出漏洞

2019-09-01 约 2972 字 预计阅读 6 分钟

声明:本文 【CVE-2017-13253 Android Drm服务 堆溢出漏洞】 由作者 miy1z1ki 于 2019-09-01 10:22:00 首发 先知社区 曾经 浏览数 117 次

感谢 miy1z1ki 的辛苦付出!

0x00 前言

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

0x01 理论

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。唯一检查和目标缓冲区有关的是 第二处的检查,但是太简单不足以阻止这种问题。 第二处的检查,确认目标缓冲区在堆内,且没有超出缓冲区的边界。

0x02 动态调试

动态调试[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'

最后 找到漏洞点

0x03 结论与补丁

1

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

2

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

0x04 参考

[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

关键词:[‘安全技术’, ‘移动安全’]


author

旭达网络

旭达网络技术博客,曾记录各种技术问题,一贴搞定.
本文采用知识共享署名 4.0 国际许可协议进行许可。

We notice you're using an adblocker. If you like our webite please keep us running by whitelisting this site in your ad blocker. We’re serving quality, related ads only. Thank you!

I've whitelisted your website.

Not now