Google SafetyNet中Root检测机制安全性研究

2019-02-22 02:49朱舒阳梁彬白石磊杨超群石文昌
北京理工大学学报 2019年1期
关键词:镜像分区启动

朱舒阳, 梁彬, 白石磊, 杨超群, 石文昌

(1. 中国人民大学 数据工程与知识工程教育部重点实验室,北京 100872;2.中国人民大学 信息学院,北京 100872)

近年来,对Android进行Root的行为越来越普遍,部分用户常常出于个性化设备、安装特权应用等目的将设备Root. 设备一旦经过Root便可以进行静默安装恶意应用、窃取用户敏感数据、篡改应用程序等恶意操作[1-3], 这无疑给设备引入了很大的安全风险.

由于上述安全风险的存在,Android平台、手机厂商以及敏感应用程序不希望设备被Root[4]. 为了进一步提高设备的安全性,各级别的Root检测机制也应运而生,主要包含:第三方应用提供的应用级Root检测、设备厂商提供的厂商级检测和Google移动服务框架提供的平台级Root检测等. 相应的对抗检测的方法也应运而生,例如,文献[5-8]采用动态劫持的方式,针对应用级Root检测机制进行分析,通过修改动态链接库的加载,实现对检测函数的劫持,从而更改检测结果;文献[9-10]主要分析三星KNOX框架,利用系统漏洞,在不熔断保险丝的情况下将设备Root并进行攻击实验,逃避硬件的检测;但就目前而言拥有更高权限并适用于更大范围的平台级Root检测机制Google SafetyNet中的Root检测机制尚不明确.

针对上述问题,本文结合Root技术的相关原理,从绕开SafetyNet Root检测的角度,采用逆向工程的方法对SafetyNet 的Root检测机制进行了深入的分析,并设计了一种新的Root方法,成功绕开了SafetyNet Root的检测,以实证性地揭示了SafetyNet Root检测机制中存在的绕开风险.

1 SafetyNet Root检测机制安全分析

SafetyNet是Google移动服务框架中的数据收集模块,其主要负责向Google云服务提供与安全相关的信息,这些信息包括安全事件、日志、配置文件等. 此外,它还提供SafetyNet attestation API,用于Android Pay或其他第三方应用对设备安全状态进行检测. 总而言之,SafetyNet模块主要包含两大功能:防护来自应用和网络的威胁、提供相应API来帮助其他应用检测设备的Root[11]状态.

为了研究Google移动服务框架中SafetyNet的Root检测机制,首先从设备中提取出Google移动服务框架的框架程序,然后采用逆向工程的方法对其Root检测机制进行了深入的分析. 具体而言分析过程主要分为4步:①利用dex2jar工具将dex文件转换为jar文件[12];②利用JD-GUI反编译jar文件为java代码[13];③结合Android系统中的Dalvik虚拟机调试监控服务(DDMS)动态分析其运行的过程;④结合动态分析的结果对反编译的代码进行静态分析.

图1描述了SafetyNet的Root检测机制. 其主要包含两种检测模式:空闲模式(idle)和常规模式(normal)以及5个检测项:system_partiation_file、setuid_file、su_files、selinux_status和device_state. 其中,空闲模式下会每隔12 h对以上所有的5个检测项进行一次检查;常规模式下则在其他应用调用SafetyNet attestation API时或者最多每隔24 h对以上后3个检测项进行一次检查.

图1 SafetyNet部分框架结构Fig.1 Framework of safetyNet

① system_partiation_files检测主要通过校验系统分区的完整性,进而判断设备的Root状态. 此项检测首先遍历系统分区,运用SHA256算法计算文件的哈希树并建立索引. 然后使用HTTPS协议向Google系统完整性校验服务器(SystemIntegrityChecker,简称SIC)发送文件信息和哈希值. 最后SIC服务器对收到的信息进行校验匹配,若匹配结果一致则返回正常,否则将其记录到日志中.

② setuid_file检测主要通过检测系统中的特权文件,进而判断设备的Root状态. 此项检测首先会扫描/dev、/system等文件目录,进而分析设备中文件的uid、suid、gid、时间、安全上下文以及mod等信息,然后根据以上信息判断设备的Root情况. 例如,Root过的设备中通常会增加一些uid为0的文件.

③ su_files检测主要通过检测设备的文件系统中是否含有提权文件,进而判断设备的Root状态. 通常而言,Root过的设备中会含有su等提权程序,因此此项检测主要通过检索系统中的Shell命令执行目录(/system/bin和/system/xbin)以及挂载分区的环境变量中是否含有su等提权程序判断设备的Root情况.

④ selinux_status检测主要通过检测设备中SEAndroid的开启状态,进而判断设备的Root状态. 由于Android 5.0版本以后,引入了SEAndroid(security-enhanced Linux in Android),并启用了强制模式[14],而且Root过的设备中SEAndroid可能处于关闭状态,因此此项检测主要通过查看当前设备SEAndroid的开启状态判断设备的Root情况.

⑤ device_state检测主要通过检测设备中设备启动的系统属性值,进而判断设备的Root状态. 设备在启动的过程中,BootLoader会对系统的ro.boot.vertiedbootstate、ro.boot.veritymode、ro.oem_unlock_supported、 ro.boot.flash.locked等属性进行校验(verified boot)[15],从而得出Bootloader锁的状态,进而判断设备的Root状态. 例如,在设备启动的过程中,系统用绿、橙、黄、红4种不同的颜色标记设备不同的状态. 其中,绿色代表系统处于可信状态下、橙色代表设备Bootloader已解锁、黄色状态代表密钥签名非官方、红色代表OEM密钥验证失败. 如果系统设备状态呈现为橙色、黄色或红色,那么意味着此设备存在很大的可能被Root了.

综上所述,SafetyNet 对设备Root状态的检测其实是对如下5个检测项:system_partiation_files、setuid_file、su_files、selinux_status和device_state的检测. 其中空闲模式下会对以上所有的5个检测项进行检查,常规模式下仅对以上后3个检测项进行检查.

2 SafetyNet Root检测的绕开方案

目前SafetyNet的Root检测机制的实质是对如下5项的检测:system_partiation_files、setuid_file、su_files、selinux_status和device_state. 也就是说一旦这5个检测项被绕开,SafetyNet的Root检测也将不攻自破. 然而传统的Root方法需要修改boot分区和system分区[16],由此而引入的Root特性很容易就能被SafetyNet检测出来. 于是本文据此设计了一种新的Root方案,通过修改启动分区中的SEAndroid相关文件和初始化的脚本文件,绕开了SafetyNet的Root检测机制,进而揭示了SafetyNet Root安全检测机制中存在的安全风险.

2.1 SafetyNet Root检测绕开方案概述

SafetyNet Root检测共包含5个检测项. 其中常规模式下主要对su_files、selinux_status和device_state进行检测;空闲模式下不仅检测了以上3项,而且还检测了system_partiation_files和 setuid_file. 根据其检测特征,本文设计了一种绕开SafetyNet Root检测的方案:通过修改启动分区和数据分区改变设备的启动流程,增加挂载和启动隐藏特征程序,实现对目标设备的Root,同时绕开SafetyNet Root检测.

图2描绘了Android系统的启动流程. 首先,启动设备的第一个启动程序Bootloader为后续的内核启动完成初始化;然后启动Linux kernel完成软硬件环境的设置. 其中,在启动Linux kernel的过程中,首先会加载驱动程序并启动Init初始化程序、创建并挂载Linux根文件系统,然后会读取解析硬件相关的Init脚本文件. 本文中的方案则是修改设备的启动过程,在此步骤额外挂载包含了提权程序和隐藏Root特征程序的提权镜像并启动监测Zygote的Hide程序. 每当要启动的应用是要屏蔽的应用(如Android Pay、使用SafetyNet attestation API的应用程序)时,则通过Hide程序卸载提权镜像、清除Shell命令调用的环境变量、重置设备的状态,以达到隐藏设备状态、绕开Root特征检测的目的.

图2 本文的Root方案Fig.2 Root project

2.2 实现细节

为了揭示SafetyNet Root安全检测机制中存在的安全风险,本文设计了一种Root方法,并据此成功绕开了SafetyNet Root的检测,以实证性地揭示了SafetyNet Root检测机制中存在的绕开风险. 其中本文Root方法主要在如图3所示设备启动分区文件系统的基础上,主要做了3方面的工作:①为了隐藏提权相关的文件,防止Root检测程序在文件扫描的过程中找到Root设备的特征,本文通过cpio命令将提权相关的文件制作成镜像文件,并将其命名为“su.img”;②为了消除Root对系统分区的影响,本文清除了系统环境变量下和系统库文件目录下与提权相关的相关文件,使得系统分区恢复为初始状态;③为了挂载镜像和启动Hide程序,本文对启动镜像内Ramdisk分区的初始化脚本进行了修改,为其增加了提权镜像的挂载和隐藏程序.

图3 启动分区文件系统Fig.3 Boot partition filesystem

2.2.1提权镜像制作

为了隐藏提权相关的文件,防止Root检测程序在文件扫描的过程中找到Root设备与提权程序相关的特征,需要利用cpio命令将提权程序(su)、隐藏程序(Hide)、重置系统属性程序(Resetprop)、处理镜像和设置SEAndroid策略的工具文件sukernel、sepolicy-inject、BusyBox及其他脚本文件制作为提权镜像文件,在本文中将其命名为“su.img”. 其中,Hide和Resetprop是隐藏Root特征最为重要的两个程序.

Hide程序在此过程中主要负责对Zygote进程进行监控,并根据黑名单列表卸载提权镜像,并重新读取设备中挂载分区的信息,从而清除提权程序执行的环境变量,屏蔽黑名单程序使用Shell命令的Root检测. 其中黑名单程序通常为Root检测程序,默认把Google移动服务框架程序列入黑名单.

Resetprop程序监测所要启动的应用程序,当黑名单应用启动时,通过动态劫持的方法将系统属性文件只读"ro"方式打开改为读写"rw",从而可以通过动态劫持的方式重置部分代表着设备不同状态(Bootloader是否解锁、是否运行了可信的Bootloader等)的系统属性如ro.boot.flash.locked,ro.boot.veritybootstate等,以绕开SafetyNet模块中的device_state项检测.

2.2.2Root特征清除

Android的根文件系统如图4所示,通常Root方案需要修改系统分区和启动分区:在系统分区加入提权程序su和守护程序daemonsu;修改启动分区的初始化脚本init.rc、安全策略sepoicy和安全上下文. 所以为了防止修改系统分区的Root方案影响SafetyNet的Root检测,需要清除系统环境变量下的提权相关文件和系统库文件目录相关文件,如系统分区Shell命令执行目录bin、xbin目的su提权程序和daemon守护进程、lib目录的libsupol.so文件、etc目录下install-recovery脚本以及所有Root权限管理软件(如SuperSU)及其配置文件,将设备的系统分区恢复为初始状态.

图4 Android根文件系统Fig.4 Android root system

2.2.3启动镜像重打包

为了挂载镜像和启动Hide程序,本文对启动镜像内Ramdisk分区的初始化脚本进行了修改,为其增加了提权镜像的挂载和隐藏程序,为此需要对启动镜像进行重打包,具体过程如下所示.

首先,在恢复模式下提取设备中的启动分区镜像并备份. 使用"dd"命令拷贝设备中的块文件,例如"dd if =/dev/block/mmcblkop19 of=/data/boot.img",将启动分区镜像提权并存储到数据分区. 然后,使用镜像处理工具解包镜像并存放在临时目录,修改启动分区Ramdisk目录中的初始化脚本init.rc,即添加新初始化脚本,并在其中设置post-fs动作列表的触发器. post-fs在初始化过程中的设置系统分区目录权限阶段执行Shell脚本,配置SEAndroid安全策略文件sepolicy,循环挂载第一步制作好的提权镜像. 再运行Shell脚本将系统分区中的命令执行路径(/system/bin)软连接到提权镜像挂载到内存的执行路径(/sbin),然后以绑定(mount bind)方式挂载文件目录并设置权限,启动提权程序的守护进程(daemonsu). 这种方式不需要修改系统分区,通过在初始化阶段挂载数据分区的提权镜像并初始化,在Android系统中执行提权程序将设备Root. 随后启动上文提到的隐藏Root的程序Hide,其设置一个黑名单应用的列表屏蔽Root检测程序,对Zygote要启动的应用程序进行监控. 当启动黑名单的应用程序时,卸载提权镜像并重新读取设备的挂载的分区,并调用Resetprop程序重新设置设备的系统属性值. 在设备的初始化阶段,启动Hide程序清除提权程序执行的环境变量,屏蔽黑名单程序,阻止它们使用Shell命令的Root检测,从而隐藏Root痕迹.

3 实 验

从Google Play中选取了3个利用SafetyNet Root检测机制进行设备Root状态检测的知名应用程序Android Pay、Pokemon Go和SafetyNet attest,分别利用传统的Root方案和本文设计的Root方案设计了3组对比性攻击实验,实验结果表明:虽然SatfetyNet Root检测可以检测出传统的Root方法,但其检测机制实现中仍存在有较高的安全风险,难以检测设计的Root方法.

3.1 实验设置

如表1所示,列出了本文的实验环境. 首先,在采用传统Root方法进行Root的指定手机设备上,安装通过SafetyNet Root检测机制进行Root检测的应用,查看设备Root状态的检测效果;然后,在采用本文Root方法进行Root的指定手机设备上,安装相同的应用进行测试,查看设备Root状态的检测结果.

表1 实验环境

3.2 实验过程

从Google Play中选取了3个利用SafetyNet Root检测机制进行设备Root状态检测的知名应用程序Android Pay、Pokemon Go和SafetyNet attest,并利用传统的Root方案和本文设计的Root方案设计了3组攻击性实验,实验步骤如下所示.

① 首先解开Nexus 5的Bootloader锁,装入定制的第三方恢复系统,然后使用此前修改系统分区的Root方案将设备Root. 此方案运行在恢复模式下,装入制作好的安装包,将所需的Root相关文件装入设备的系统分区并重打包启动分区,使之启动提权守护程序daemons. Android 5.0之后提权程序su将应用程序发出的提权命令转发给daemonsu,由daemonsu来执行Root权限的命令,此方案需要将提权文件su以及提权守护程序daemonsu装入系统分区的命令执行目录(/system/bin). Google移动服务框架的SafetyNet模块Root检测中,su_files和device_state项检测可以检测设备系统分区的命令执行目录和设备的系统属性,因为提权程序su存放在系统分区的命令执行目录并且设备的Bootloader已经解锁以橙色状态启动.

② 继续在恢复模式下使用本文改进无需修改系统分区的Root方案. 如本文2.2节所述,首先打包并制作提权镜像,即将提权相关程序(su、daemonsu)、隐藏Root特征程序(Hide)、重置系统属性程序(Resetprop)等打包为su.img提权镜像并存放到数据分区. 然后清除掉此前Root方案的痕迹,删掉系统分区Root相关的文件. 紧接着重打包启动镜像,使用dd命令提取启动分区的镜像并放在数据分区,解压释放启动镜像,修改其初始化的脚本文件,添加挂载提权镜像和Hide隐藏程序的入口点,设置黑名单的应用列表(默认将谷歌移动服务加入其中). 最后重新打包启动分区的镜像附上签名和加密选项,使其启动加载器中正常启动,将重打包的镜像modboot.img,在fastboot模式下刷入设备中的启动分区.

③ 设备启动后,连接电脑ADB Shell命令,调用提权程序su,测试设备是否被Root. 确定设备Root后,分别打开应用Android Pay、Pokemon Go和SafetyNet attest判断设备的Root状态.

按照如上步骤,分别利用Android Pay、Pokemon Go和SafetyNet attest对传统Root方案和本文Root方案进行检测. 如图5~7所示,分别是Android Pay、Pokemon Go和SafetyNet attest对传统Root方案和本文Root方案的检测结果.

图5 Android Pay SafetyNet Root绕开Fig.5 Bypass Android Pay SafetyNet Root ditection

图6 Pokemon Go SafetyNet Root绕开Fig.6 Bypass Pokemon Go SafetyNet Root detection

图7 SafetyNet attest SafetyNet Root绕开Fig.7 Bypass SafetyNet attest SafetyNet Root detection

如表2所示,Android Pay、Pokemon Go和SafetyNet attest均成功地检测出了传统Root方案对设备的Root,却没有检测出本文设计的Root方法对设备的Root. 实验结果表明:虽然SatfetyNet Root检测可以检测出传统的Root方法,但其检测机制实现中仍存在有较高的安全风险,难以检测本文设计的Root方法.

表2 实验结果

本文利用本文设计的Root方案对目前最新版的Android Pay、Pokemon Go和SafetyNet attest等使用了SafetyNet Root检测机制进行Root检测的常见应用进行了攻击实验,实验结果表明:Google平台级SafetyNet Root检测机制中存在有绕开风险,难以检测本文设计的Root方案.

4 结束语

本文采用逆向工程的方法对Google SafetyNet的Root检测机制进行了深入分析,并通过攻击性实验,实证性地揭示了Google平台级SafetyNet Root检测机制中存在有绕开风险,难以检测本文设计的Root方案,进而指出当前的SafetyNet Root检测机制的安全性需要进一步提高. 目前较为理想的缓解策略是将软件与硬件相结合,通过硬件保证软件在安全环境下,执行Root检测. 但是设备上硬件检测机制标准的统一还需要漫长的时间,所以Android设备上Root检测机制风险仍有可能长期存在.

猜你喜欢
镜像分区启动
贵州省地质灾害易发分区图
上海实施“分区封控”
镜像
《悦读·家》暨“悦读·家@万家”活动启动
镜像
大型数据库分区表研究
电启动机的正确使用
镜像
俄媒:上合组织或9月启动扩员
神探出手,巧破分区离奇失踪案