基于自适应热补丁的Android内核漏洞生态修复方案

作者:张煜龙   陈越  包沉浮  夏良钊  郑龙日  卢永强   韦韬(Baidu X-Lab)

1. 背景

Android内核漏洞严重影响了Android平台的安全。一旦内核被攻破,所有依赖内核完整性的安全机制都岌岌可危(比如加密、进程隔离、支付、指纹验证等)。作为Android平台最后的防线,TrustZone也会受到威胁,因为内核可以从很多被信任的接口向TrustZone发起攻击。因此,理想情况下Android生态圈应该及时消灭内核漏洞。然而从Google的Android Security Bulletin上看,Android内核漏洞的数量呈飞快上涨的趋势(Figure 1所示)。虽然这代表着人们对内核安全的投入逐渐增大,但与此同时这些漏洞一旦公诸于众,在很长一段时间都难以得到修复,导致越来越多的可以公开利用的漏洞变成了黑产手中的利器。纵观近期几起影响全世界的重大恶意malware事件[1-4],它们都携带了root工具,并且root的方法不是源自公开的PoC,就是直接重用了一些一键root工具的库。


1

Figure 1: Number of kernel vulnerabilities disclosed in monthly Android Security Bulletin

人们经常宣称“Android容易被攻击”或者“Android不如苹果安全”。但是,Table 1列出了几乎同时期iOS和Android内核漏洞数量的比较,不难发现Android漏洞公布的频率甚至不如iOS多。虽然漏洞和漏洞并不能直接拿来比较(因为危害程度不一定一样),但至少这可以提供一个参考基准——至少苹果不是生来就安全的,而且也需要定期修复大量的内核漏洞。只是因为iOS修补漏洞比较全面和及时,让人感觉它非常“安全“。同样地,“Android不安全”并不是仅仅因为漏洞频发,更重要的还是不能像iOS这样及时地修复问题。2

  Table 1: iOS kernel vulnerabilities disclosed in each release, compared to that of Android. Note that the number of vulnerabilities may not be directly comparable due to different threat levels. But the comparison can still provide a rough sense that iOS is not born as “bullet-proof”; it is more secure just because it can get timely patches in a large scale.

2. 为何Android内核漏洞得不到及时修复?

究其原因,主要有三点:过长的补丁链,严重碎片化的生态,和生态职能不匹配。

Android的产业链过长:Android生态系统产业链非常长,从硬件厂商,到Google,到手机厂商,再到运营商。每一个环节都有自己的开发、质控、验证等复杂流程,有时候还会相互冲突。这样要消耗大量的时间和人力,而且有时甚至导致安全补丁无法下发给用户。这个冗长的修补链条如Figure 2所示。

  3Figure 2: The long patching circle of Android kernel vulnerabilities

Android系统碎片化过于严重:Android系统的碎片化是指世界上所有Android用户实际使用的Android版本、配置、驱动等等有着非常显著的差异。Android系统碎片化已经成为了业界共识,由于系统的开源性,用户、开发者、OEM厂商、运营商都可以按照自己的想法进行改造,这种“碎片化”的程度异常严重。在这种情况下,大多数手机设备厂商已经失去了为所有用户使用的Android系统提供安全补丁的能力。Figure 3所示为繁多的Android品牌和机型。

  4Figure 3: Android device fragmentation, cited from [5]

根据Google官方发布于2016年7月的统计[6] :

1)Lollipop (Android 5.0) 发布于November 2014, 但是仍然有55% 的设备低于该版本

2)Google已经停止为低于4.4版本的Android提供补丁,但仍有23%的设备低于该版本

而在中国,由于对Google的访问被阻隔,这一问题更加严重。通过随机采样大约200万装有Baidu App的设备的手机,我们发现:

1)80% 的设备低于Android 5.0

2)42% 的设备低于Android 4.4

生态职能不匹配:其实最容易推进修复的是手机厂商,但大部分手机厂商自身的碎片化现象非常严重,导致了手机厂商没有足够的资源和精力去修复漏洞。而对于安全厂商来讲,修复漏洞也是一件很尴尬的事情。在正常情况下,安全厂商是没有系统授权的,除非先进行root。即使root,安全厂商仍然要面临不同手机品牌更加严重的碎片化挑战。

3. 案例分析

具体看3个著名的能被用来进行跨平台root的漏洞:第一个是CVE-2014-3153(“Towelroot”),公开和修补时间为June 3, 2014 [7] ;第二个是CVE-2015-3636(“Ping Pong Root”),公开和修补时间为 September 9, 2015 [8] ;第三个是CVE-2015-1805,公开时间为April 3, 2014 [9] ,但直到March 18, 2016才在Android平台上修复 [10] 。Figure 4 所示为这三个漏洞距今(2016年7月)的时间跨度。其中最短的也达到了120天。

 5 Figure 4: Days from advisory publication date to now (July 2016) for the three famous “one-to-root-all” kernel vulnerabilities

6

Figure 5: Vulnerability statistics collected from Chinese Android device

Figure 5 所示为这三个漏洞在装有百度App的手机上的随机采样结果。可见虽然这么长时间过去了,仍然有非常多的设备没有得到漏洞修复。

4. AdaptKpatch: 自适应内核热修补框架

如何解决Android漏洞得不到及时修补的问题?对于跳过冗长补丁链这个问题,学术界工业界已经给出了一些方案,比如kpatch [11] , ksplice [12] , kGraft [13] , 以及Linux upstream的livepatch [14] . 但是这些方案并没有解决碎片化和能力失配的问题。更本质地说,这些方案都依赖源码,对于没有源码的第三方不适用,更遑论补丁的广泛适配。因此在第七届HITB 会议上[15] , 我们提出了内核自适配修补的方案,称作AdaptKpatch。其大致流程如Figure 6所示。

  7Figure 6: Adaptive kernel live patching procedures without phone vendors’ cooperation [15]

简单来说,该框架在手机上探测存在的内核漏洞,向用户征询是否修复。得到允许后,该框架root手机并收集必要的信息,包括kernel versions, symbol addresses, symbol CRCs等。取决于设备是否提供insmod或者(k)mem 设备,框架会下载对应的模板进行数据结构填充和symbol解析,并视情况对内核的一些动态校验机制进行放宽。这些步骤完成后即完成补丁模板的适配,所生成的补丁即可插入该设备的内核执行修补操作。具体的修补操作和其他热修补方案无异,比如函数替换、调用替换、原地修补等,此处不再赘述。 注意该框架只是热修复,因此每次重启都要重新加载热补丁。如果某次启动发现前次启动因为热补丁而出现崩溃,则放弃加载热补丁回滚到正常启动。

  8Figure 7: Adaptive kernel live patching framework with phone vendor’s cooperation

如果手机厂商愿意和第三方协作修复时,该框架可以最大化发挥效益。厂商不再需要花资源和精力修补漏洞,只需要提供预制的内核修补机制/能力,而安全厂商不需要再root手机和探测漏洞,可以专注在修补漏洞上。这一合作模式如Figure 7所示。基于这一合作联盟,可有如下安全机制保证补丁的安全:

1)厂商资质校验:只有有能力和名声好的厂商方可进入合作联盟,各厂商所发补丁需签名验证;

2)补丁审计:补丁发放前需要经过联盟里别的厂商进行安全审计,防止引入新的漏洞或者后门;

3)评价排名:类似于App Store,用户可以对某个补丁进行稳定性等方面的评价。其他用户可以根据补丁的既有评价选择打还是不打。

5. LuaKpatch: 更灵活迅速,但是更严格

5.1 设计原理

AdaptKpatch已经能很好地解决Android内核漏洞长期得不到修复的问题了,但补丁仍然需要通过审计才能保证安全。因此我们还提出了另一个方案:LuaKpatch。其原理是用类型安全(type-safe)的动态语言引擎(比如Lua)来执行补丁。这样一来补丁灵活易维护,同时其执行可以严格控制在引擎所允许的范围内,也不担心因为溢出、空指针引用等问题被攻击劫持。

9

Figure 8: Kernel functions take input from either arguments or external data reading (e.g., copy_from_user) as shown in (a). Most Android kernel exploits inject malicious input into the target function and hijack the control flow to achieve arbitrary code execution, as shown in (b). So, by hooking the data input entries and validating the input as shown in (c), we can block most of the kernel exploits.

Figure 8 所示为绝大部分kernel漏洞攻击的原理。一般而言攻击者都需要灌入恶意的输入来实现控制流劫持。因此LuaKpatch只需要在函数入口和函数中读数据的位置插入检查即可,不需要给予补丁任意hook的能力。

具体来说,我们有如下限制:

1)补丁可以hook函数入口,检查参数和环境

2)补丁也可以hook函数中的调用,前提是该调用返回的是状态值(下文只有条件依赖),而不是指针等有数值依赖的返回值(copy_from_user满足这一条件,返回是成功或失败的状态值)

3)补丁可以任意读,但不可写,更不能发送数据出去

4)补丁完成条件判断后,可返回正常或异常。这样当遇到攻击输入时,内核随后的执行不会触及会被exploit的代码。

为了更直观地理解,Figure 9给出了一个具体的例子。fun调用了foo 和bar。其中fun的入口是允许被hook的,fun中foo的调用前和调用后也是允许被hook的,因为foo返回值只有条件依赖,而bar是不能被hook的,因为下文产生了数值依赖(其中一个域被用作函数指针进行调用)。这样一来,攻击者即使控制了补丁,也没法通过返回恶意数值来实现任意代码执行。

  10Figure 9: A running example to illustrate which functions can be hooked and which cannot

我们为返回值只有条件依赖的函数生成了一个白名单,对于一个目标函数,其调用只有在落在这个集合里时才能被hook检查。而且注意hook发生在目标函数调用处,而不是被调用函数的入口和返回处。我们称能在函数口作检查即可修复的漏洞为Type I vulnerabilities,而如果还需要在函数体内对调用做hook检查的,则称为Type II vulnerabilities。稍后我们会给出两种漏洞的数目。

5.2 实现

我们基于Lua实现了LuaKpatch,为了将Lua集成在内核里,很多改变借鉴了lunatik-ng [16] . 比如数值应该修改为整型而非浮点型。LuaKpatch的代码大约1万1千行,其中核心的与修补有关的代码大概600行。编译后LuaKpatch模块大小为800KB。厂商可以提前将这一模块集成于内核里,或者可以借用AdaptKpatch里所介绍的内核模块自适应加载方法,后天由第三方root手机后插入内核。

LuaKpatch为Lua形式的内核补丁提供了如下接口:符号搜索、hook(在指定位置插入trampoline,保存现场并调用Lua补丁的handler进行判定,然后回到指定位置的下一指令)、内存读取、thread信息获取。 Figure 10 为利用LuaKpatch修补Towelroot漏洞的实例。  11

Figure 10: Sample Lua patch to fix one of the vulnerable conditions of CVE-2014-3153, known as “Towelroot”

5.3 有效性分析

如Table 2所示,近年几乎有代表性的能用来root手机的内核漏洞LuaKpatch都能修补。前面有提到能在函数口作检查即可修复的漏洞为Type I vulnerabilities,而如果还需要在函数体内对调用做hook检查的,则称为Type II vulnerabilities。Figure 11提供了这两类漏洞的比例,大部分漏洞还是可以简单地通过在函数口进行检查来修补的。万一遇到LuaKpatch不能修补的漏洞,可以回滚到AdaptKpatch方案进行修补。

12  Table 2: Android root vulnerabilities that have been verified to be protectable by LuaKpatch. Most of the vulnerabilities are Type I vulnerabilities (those that can be patched by simply hooking the entry of the vulnerable functions), but the highlighted/colored ones are Type II vulnerabilities (those that also need to hook the invocations that return status code).

 13 Figure 11: All 21 vulnerabilities that we collected can be patched by LuaKpatch. Among them, 16 are Type I vulnerabilities, and 5 are Type II vulnerabilities.

5.4 性能测试

我们用CF-Bench [17] 在装有Android 4.4的Nexus 5 上测了四种情况:不打补丁,用LuaKpatch修补Towelroot,用LuaKpatch修补Ping Pong Root,用LuaKpatch修补二者。 结果如Figure 12 所示——LuaKpatch完全没有带来任何性能损耗。这是预料之中的,因为提权内核漏洞往往出现在平时无法触及的执行路径上,修补后并不对正常执行产生影响。

 14Figure 12: Performance scores obtained from CF-Bench

尽管如此,我们还是想知道一旦被触及,LuaKpatch的开销是多少。因此我们测试了chmod 系统调用的原始执行时间、打上补丁但是补丁直接return的执行时间(相当于只是现场保存恢复和Lua进出的开销)、打上补丁且补丁做一次条件判断的执行时间、打上补丁且补丁做一次内存地址读的执行时间、打上补丁且补丁做一些列综合操作(变量操作、内存读、条件判断等,模拟一个比Towelroot和Ping Pong Root补丁更复杂的补丁的操作)的执行时间,其结果如Figure 13所示。可见即使在复杂补丁的执行下,也只多了3.74微秒的开销。这仅仅是chmod正常执行的4%。

  15Figure 13: Execution time of chmod with/without the patches

基本上,在我们的测试里,LuaKpatch补丁都能在5微秒的开销里完成任务。未来我们会把LuaKpatch的引擎换成LuaJIT [19] ,性能会得到进一步提升。

6. 总结

基于本文所述AdaptKpatch和LuaKpatch技术,Android修补流程可以改进成如Figure 14所示。对于非常严重的问题、需要几天内推出补丁的,可以用LuaKpatch;对于LuaKpatch不能解决的问题,可以经过补丁审计后用AdaptKpatch方案推出;最终冷补丁可以永久性地修补相应问题。基于这一流程,Android内核漏洞长期难以被修复的问题可以得到妥善解决。

16 Figure 14: The patching circle in the open collaborative patching ecosystem. From left to right, it takes more time for patches to land on user devices to take effect. And from left to right, the safety level also decreases. Note that AdaptKpatch can perfectly fix more issues than LuaKpatch, so when LuaKpatch is not an ideal choice, AdaptKpatch can be a great alternative.

我们呼吁Android生态圈建立一个开放协作的联盟,包括手机厂商、安全厂商、和其他Android社区的成员。这个联盟将具有非常重要的意义:

1)漏洞越来越多也越来越复杂,在LuaKpatch和AdaptKpatch提供的和作平台上,大家可以分担修补的难题,共享其成;

2)对于AdaptKpatch的场景,越多的参与者意味着更多更安全的补丁审计;

3)最后但也是最重要的,整个Android生态圈应该一起将这个热修补流程维护成统一的标准,防止在当前设备碎片化之上又引入了修补方案的碎片化,导致最终还是各方分散力量修补漏洞、难以规模化有效地解决问题。

参考文献:

[1] http://www.cmcm.com/blog/en/security/2015-09-18/799.html

[2] https://www.fireeye.com/blog/threat-research/2015/10/kemoge_another_mobi.html

[3] https://www.bluecoat.com/security-blog/2016-04-25/android-exploit-delivers-dogspectus-ransomware

[4] https://blog.checkpoint.com/wp-content/uploads/2016/07/HummingBad-Research-report_FINAL-62916.pdf

[5] http://opensignal.com/reports/2015/08/android-fragmentation

[6] https://developer.android.com/about/dashboards/index.html

[7] https://github.com/torvalds/linux/commit/e9c243a5a6de0be8e584c604d353412584b592f8

[8] https://source.android.com/security/bulletin/2015-09-01.html

[9] https://github.com/torvalds/linux/commit/f0d1bec9d58d4c038d0ac958c9af82be6eb18045

[10] http://source.android.com/security/advisory/2016-03-18.html

[11] https://github.com/dynup/kpatch

[12] https://www.ksplice.com/doc/ksplice.pdf

[13] http://events.linuxfoundation.org/sites/events/files/slides/kGraft.pdf

[14] http://lxr.free-electrons.com/source/include/linux/livepatch.h

[15] https://conference.hitb.org/hitbsecconf2016ams/sessions/adaptive-android-kernel-live-patching

[16] https://github.com/lunatik-ng/lunatik-ng

[17] https://play.google.com/store/apps/details?id=eu.chainfire.cfbench&hl=en

[18] https://httpd.apache.org/docs/2.4/programs/ab.html

[19] http://luajit.org

Bookmark the permalink.

Comments are closed.