原文地址: http://android-developers.blogspot.com/2016/05/hardening-media-stack.html
Posted by Dan Austin and Jeff Vander Stoep, Android Security team
为了使Android更加安全,我们鼓励并奖励那些发现漏洞的研究者。2015年,Google修复了mediaserver’s libstagefright的一系列bug。我们在2015年8月和9月的security bulletin上发布了这些问题的更新。
除了每个月基本的解决问题外,我们也在做一个新的安全功能,以强化现有的安全模型,并提供额外的深度防御。这些防御措施试图达到两个目的:
- Prevention:修复bug,防止它们变成漏洞
- Containment:隔离那些处理不受信任内容的组件,以保护系统
Prevention
libstagefright中发现的大部分漏洞都是由无符号整型溢出引起的堆溢出。一些的整型溢出使得攻击者可以分配一个比实际到达数据所需要空间小的缓冲区,从而引起堆的缓冲区溢出。
无符号整型溢出造成的结果是非常明确的,但随后引发的行为可能是不期望的或者不安全的。相比之下,在C/C++中,有符号整型溢出被认为是不明确的行为,这意味着溢出造成的后果是没保证的,并且编译器的作者通常会从引发的后果中选择一个最快或最简单的。我们对编译器做出修改,使得编译器在有符号和无符号整型溢出时都能提供一个更安全的默认结果。
UndefinedBehaviorSanitizer(UBSan)是LLVM/Clang 编译器工具链的一部分,用于检测那些未定义的或意料之外的行为。UBSan会检查多种类型的未定义和不安全行为,包括有符号和无符号整型溢出。这些检测会将代码添加到可运行文件中,在运行时检测整型溢出。例如,图1展示的是原研究人员打上补丁后的libstagefright的MPEG4Extractor控件的parseChunk函数的源代码。这次修改,包含在图中的黑色框中,看起来防止了整型溢出。很不幸的是,SIZE_MAX和size是32位值,chunk_size是64位值,这造成不完全检测和潜在的整型溢出。在红色框中,size和chunk_size的加法可能引起整型溢出,并创建了一个比size元素小的缓冲区。随后的memcpy操作可能导致可用内存的崩溃,因为蓝色框中的size+chunk_size可能会小于size。关于这个漏洞的潜在利用向量的机制的详细描述,请看Project
Zero。
图1:源代码展示了一个微妙的无符号整型溢出
图2将上面的代码段产生的汇编语言与通过integer sanitization enabled编译的第二版本做了对比。图2红框中是引起整型溢出的加法操作。
在unsanitized版本中,size(R6)和chunk_size(R7)相加,有可能会导致R0溢出且小于size。本该按size分配的缓冲区被指定为R0的大小,然后size个比特被拷贝进去R0大小的缓冲区。如果R0小于size(R6),这会引起内存崩溃。
在sanitized版本中,size(R7)和chunk_size(R5)相加,并把结果存储到R0。然后,R0和R7比较,如果R0小于R7,程序会指向CC情况的代码,R3被设为1。如果R3为1,且设置了执行位,那么发生内存溢出,并且触发异常终止,从而避免内存崩溃。
注意,由补丁引发的不完全检测并不包含在图2中。溢出发生在缓存区分配时的加法操作。sanitized版本中增加的触发操作,把可被利用的漏洞转化为无害的异常终止。
虽然整型sanitizer原本是作为代码清洁工具的,它们有效的阻止大部分已知的libstagefright漏洞。启动整型溢出检查只是第一步。找出并修复整型溢出(大部分是不可被利用的),以防止运行时异常终止,这体现了Android`s media team的努力。大部分被发现的溢出都被修复,剩余的(主要是性能的原因)被确认和标记为安全的,以防止运行时异常终止。
在Android N中,在整个media stack中都可以进行有符号和无符号整型溢出的检测,这就包括了libstagefright。这使得利用整型溢出变得更困难,同时也可以防止将来会引进新的整型溢出Bug。
Containment
在Android M和更早的版本,mediaserver进程负责大部分和多媒体相关的任务。这以为它要访问所有和这些任务相关的权限,尽管它运行在它自己的沙盒中,但它仍需要访问大量的资源和功能。这就是为什么2015年时libstagefright Bug具有重大意义——mediaserver可以访问Android设备上一些重要的资源,包括相机,麦克风,显卡,电话,蓝牙以及网络。
根源分析表示libstagefright Bug主要发生在解析文件格式和多媒体解码器的代码中。这并不奇怪,解析复杂文件格式和编码器试图加速是很困难的,大量的边缘情况使得代码容易受到偶然的和恶意的畸形输入攻击。
然而,多媒体解析器不需要访问大部分mediaserver持有的权限。基于这个原因,media ream重新设计了Android N的mediaserver,使得它更好的遵循最少特权原则。图3展示了整块mediaserver和它的权限被如何分割,这种分割是基于下面的方法:
- 解析代码被移进持有很少或没有权限的非特权沙盒
- 需要敏感权限的组件被移进了分离沙盒,这个沙盒只能访问该组件所需要的特定资源。例如,只有cameraserver可能访问相机,只有audioserver可能访问蓝牙,只有drmserver可能访问DRM资源。
将Android N和更早期版本做对比,libstagefright Bug带来的潜在影响展示了这种策略的价值。在取得libstagefright可执行代码前允许整块mediaserver访问所有的权限和可用资源(包括显卡驱动,相机驱动,sockets等),会暴露大范围的内核攻击面。
在Android N中,libstagefright运行在只能访问非常少权限的多媒体编码器沙盒中。SELinux阻止对相机、麦克风、图片、电话、蓝牙、网络的访问和动态代码的加载。Seccomp进一步限制与内核的交互。这意味着libstagefright通过减少内核暴露的攻击面,使得攻击者能访问的权限更少,也减缓了特权增加。
Conclusion
这个多媒体强化项目是一场持续不断的努力,它集中于把功能移进低权限的沙盒,并进一步减少授予这些沙盒的权限。虽然这里讨论的技术被用在Android 多媒体框架,但它同时适用于Android代码库。这些强化技术或其他技术,正在积极地应用于Android的其他组件。