朱舒陽, 梁彬, 白石磊, 楊超群, 石文昌
(1. 中國人民大學 數(shù)據(jù)工程與知識工程教育部重點實驗室,北京 100872;2.中國人民大學 信息學院,北京 100872)
近年來,對Android進行Root的行為越來越普遍,部分用戶常常出于個性化設(shè)備、安裝特權(quán)應用等目的將設(shè)備Root. 設(shè)備一旦經(jīng)過Root便可以進行靜默安裝惡意應用、竊取用戶敏感數(shù)據(jù)、篡改應用程序等惡意操作[1-3], 這無疑給設(shè)備引入了很大的安全風險.
由于上述安全風險的存在,Android平臺、手機廠商以及敏感應用程序不希望設(shè)備被Root[4]. 為了進一步提高設(shè)備的安全性,各級別的Root檢測機制也應運而生,主要包含:第三方應用提供的應用級Root檢測、設(shè)備廠商提供的廠商級檢測和Google移動服務(wù)框架提供的平臺級Root檢測等. 相應的對抗檢測的方法也應運而生,例如,文獻[5-8]采用動態(tài)劫持的方式,針對應用級Root檢測機制進行分析,通過修改動態(tài)鏈接庫的加載,實現(xiàn)對檢測函數(shù)的劫持,從而更改檢測結(jié)果;文獻[9-10]主要分析三星KNOX框架,利用系統(tǒng)漏洞,在不熔斷保險絲的情況下將設(shè)備Root并進行攻擊實驗,逃避硬件的檢測;但就目前而言擁有更高權(quán)限并適用于更大范圍的平臺級Root檢測機制Google SafetyNet中的Root檢測機制尚不明確.
針對上述問題,本文結(jié)合Root技術(shù)的相關(guān)原理,從繞開SafetyNet Root檢測的角度,采用逆向工程的方法對SafetyNet 的Root檢測機制進行了深入的分析,并設(shè)計了一種新的Root方法,成功繞開了SafetyNet Root的檢測,以實證性地揭示了SafetyNet Root檢測機制中存在的繞開風險.
SafetyNet是Google移動服務(wù)框架中的數(shù)據(jù)收集模塊,其主要負責向Google云服務(wù)提供與安全相關(guān)的信息,這些信息包括安全事件、日志、配置文件等. 此外,它還提供SafetyNet attestation API,用于Android Pay或其他第三方應用對設(shè)備安全狀態(tài)進行檢測. 總而言之,SafetyNet模塊主要包含兩大功能:防護來自應用和網(wǎng)絡(luò)的威脅、提供相應API來幫助其他應用檢測設(shè)備的Root[11]狀態(tài).
為了研究Google移動服務(wù)框架中SafetyNet的Root檢測機制,首先從設(shè)備中提取出Google移動服務(wù)框架的框架程序,然后采用逆向工程的方法對其Root檢測機制進行了深入的分析. 具體而言分析過程主要分為4步:①利用dex2jar工具將dex文件轉(zhuǎn)換為jar文件[12];②利用JD-GUI反編譯jar文件為java代碼[13];③結(jié)合Android系統(tǒng)中的Dalvik虛擬機調(diào)試監(jiān)控服務(wù)(DDMS)動態(tài)分析其運行的過程;④結(jié)合動態(tài)分析的結(jié)果對反編譯的代碼進行靜態(tài)分析.
圖1描述了SafetyNet的Root檢測機制. 其主要包含兩種檢測模式:空閑模式(idle)和常規(guī)模式(normal)以及5個檢測項:system_partiation_file、setuid_file、su_files、selinux_status和device_state. 其中,空閑模式下會每隔12 h對以上所有的5個檢測項進行一次檢查;常規(guī)模式下則在其他應用調(diào)用SafetyNet attestation API時或者最多每隔24 h對以上后3個檢測項進行一次檢查.
圖1 SafetyNet部分框架結(jié)構(gòu)Fig.1 Framework of safetyNet
① system_partiation_files檢測主要通過校驗系統(tǒng)分區(qū)的完整性,進而判斷設(shè)備的Root狀態(tài). 此項檢測首先遍歷系統(tǒng)分區(qū),運用SHA256算法計算文件的哈希樹并建立索引. 然后使用HTTPS協(xié)議向Google系統(tǒng)完整性校驗服務(wù)器(SystemIntegrityChecker,簡稱SIC)發(fā)送文件信息和哈希值. 最后SIC服務(wù)器對收到的信息進行校驗匹配,若匹配結(jié)果一致則返回正常,否則將其記錄到日志中.
② setuid_file檢測主要通過檢測系統(tǒng)中的特權(quán)文件,進而判斷設(shè)備的Root狀態(tài). 此項檢測首先會掃描/dev、/system等文件目錄,進而分析設(shè)備中文件的uid、suid、gid、時間、安全上下文以及mod等信息,然后根據(jù)以上信息判斷設(shè)備的Root情況. 例如,Root過的設(shè)備中通常會增加一些uid為0的文件.
③ su_files檢測主要通過檢測設(shè)備的文件系統(tǒng)中是否含有提權(quán)文件,進而判斷設(shè)備的Root狀態(tài). 通常而言,Root過的設(shè)備中會含有su等提權(quán)程序,因此此項檢測主要通過檢索系統(tǒng)中的Shell命令執(zhí)行目錄(/system/bin和/system/xbin)以及掛載分區(qū)的環(huán)境變量中是否含有su等提權(quán)程序判斷設(shè)備的Root情況.
④ selinux_status檢測主要通過檢測設(shè)備中SEAndroid的開啟狀態(tài),進而判斷設(shè)備的Root狀態(tài). 由于Android 5.0版本以后,引入了SEAndroid(security-enhanced Linux in Android),并啟用了強制模式[14],而且Root過的設(shè)備中SEAndroid可能處于關(guān)閉狀態(tài),因此此項檢測主要通過查看當前設(shè)備SEAndroid的開啟狀態(tài)判斷設(shè)備的Root情況.
⑤ device_state檢測主要通過檢測設(shè)備中設(shè)備啟動的系統(tǒng)屬性值,進而判斷設(shè)備的Root狀態(tài). 設(shè)備在啟動的過程中,BootLoader會對系統(tǒng)的ro.boot.vertiedbootstate、ro.boot.veritymode、ro.oem_unlock_supported、 ro.boot.flash.locked等屬性進行校驗(verified boot)[15],從而得出Bootloader鎖的狀態(tài),進而判斷設(shè)備的Root狀態(tài). 例如,在設(shè)備啟動的過程中,系統(tǒng)用綠、橙、黃、紅4種不同的顏色標記設(shè)備不同的狀態(tài). 其中,綠色代表系統(tǒng)處于可信狀態(tài)下、橙色代表設(shè)備Bootloader已解鎖、黃色狀態(tài)代表密鑰簽名非官方、紅色代表OEM密鑰驗證失敗. 如果系統(tǒng)設(shè)備狀態(tài)呈現(xiàn)為橙色、黃色或紅色,那么意味著此設(shè)備存在很大的可能被Root了.
綜上所述,SafetyNet 對設(shè)備Root狀態(tài)的檢測其實是對如下5個檢測項:system_partiation_files、setuid_file、su_files、selinux_status和device_state的檢測. 其中空閑模式下會對以上所有的5個檢測項進行檢查,常規(guī)模式下僅對以上后3個檢測項進行檢查.
目前SafetyNet的Root檢測機制的實質(zhì)是對如下5項的檢測:system_partiation_files、setuid_file、su_files、selinux_status和device_state. 也就是說一旦這5個檢測項被繞開,SafetyNet的Root檢測也將不攻自破. 然而傳統(tǒng)的Root方法需要修改boot分區(qū)和system分區(qū)[16],由此而引入的Root特性很容易就能被SafetyNet檢測出來. 于是本文據(jù)此設(shè)計了一種新的Root方案,通過修改啟動分區(qū)中的SEAndroid相關(guān)文件和初始化的腳本文件,繞開了SafetyNet的Root檢測機制,進而揭示了SafetyNet Root安全檢測機制中存在的安全風險.
SafetyNet Root檢測共包含5個檢測項. 其中常規(guī)模式下主要對su_files、selinux_status和device_state進行檢測;空閑模式下不僅檢測了以上3項,而且還檢測了system_partiation_files和 setuid_file. 根據(jù)其檢測特征,本文設(shè)計了一種繞開SafetyNet Root檢測的方案:通過修改啟動分區(qū)和數(shù)據(jù)分區(qū)改變設(shè)備的啟動流程,增加掛載和啟動隱藏特征程序,實現(xiàn)對目標設(shè)備的Root,同時繞開SafetyNet Root檢測.
圖2描繪了Android系統(tǒng)的啟動流程. 首先,啟動設(shè)備的第一個啟動程序Bootloader為后續(xù)的內(nèi)核啟動完成初始化;然后啟動Linux kernel完成軟硬件環(huán)境的設(shè)置. 其中,在啟動Linux kernel的過程中,首先會加載驅(qū)動程序并啟動Init初始化程序、創(chuàng)建并掛載Linux根文件系統(tǒng),然后會讀取解析硬件相關(guān)的Init腳本文件. 本文中的方案則是修改設(shè)備的啟動過程,在此步驟額外掛載包含了提權(quán)程序和隱藏Root特征程序的提權(quán)鏡像并啟動監(jiān)測Zygote的Hide程序. 每當要啟動的應用是要屏蔽的應用(如Android Pay、使用SafetyNet attestation API的應用程序)時,則通過Hide程序卸載提權(quán)鏡像、清除Shell命令調(diào)用的環(huán)境變量、重置設(shè)備的狀態(tài),以達到隱藏設(shè)備狀態(tài)、繞開Root特征檢測的目的.
圖2 本文的Root方案Fig.2 Root project
為了揭示SafetyNet Root安全檢測機制中存在的安全風險,本文設(shè)計了一種Root方法,并據(jù)此成功繞開了SafetyNet Root的檢測,以實證性地揭示了SafetyNet Root檢測機制中存在的繞開風險. 其中本文Root方法主要在如圖3所示設(shè)備啟動分區(qū)文件系統(tǒng)的基礎(chǔ)上,主要做了3方面的工作:①為了隱藏提權(quán)相關(guān)的文件,防止Root檢測程序在文件掃描的過程中找到Root設(shè)備的特征,本文通過cpio命令將提權(quán)相關(guān)的文件制作成鏡像文件,并將其命名為“su.img”;②為了消除Root對系統(tǒng)分區(qū)的影響,本文清除了系統(tǒng)環(huán)境變量下和系統(tǒng)庫文件目錄下與提權(quán)相關(guān)的相關(guān)文件,使得系統(tǒng)分區(qū)恢復為初始狀態(tài);③為了掛載鏡像和啟動Hide程序,本文對啟動鏡像內(nèi)Ramdisk分區(qū)的初始化腳本進行了修改,為其增加了提權(quán)鏡像的掛載和隱藏程序.
圖3 啟動分區(qū)文件系統(tǒng)Fig.3 Boot partition filesystem
2.2.1提權(quán)鏡像制作
為了隱藏提權(quán)相關(guān)的文件,防止Root檢測程序在文件掃描的過程中找到Root設(shè)備與提權(quán)程序相關(guān)的特征,需要利用cpio命令將提權(quán)程序(su)、隱藏程序(Hide)、重置系統(tǒng)屬性程序(Resetprop)、處理鏡像和設(shè)置SEAndroid策略的工具文件sukernel、sepolicy-inject、BusyBox及其他腳本文件制作為提權(quán)鏡像文件,在本文中將其命名為“su.img”. 其中,Hide和Resetprop是隱藏Root特征最為重要的兩個程序.
Hide程序在此過程中主要負責對Zygote進程進行監(jiān)控,并根據(jù)黑名單列表卸載提權(quán)鏡像,并重新讀取設(shè)備中掛載分區(qū)的信息,從而清除提權(quán)程序執(zhí)行的環(huán)境變量,屏蔽黑名單程序使用Shell命令的Root檢測. 其中黑名單程序通常為Root檢測程序,默認把Google移動服務(wù)框架程序列入黑名單.
Resetprop程序監(jiān)測所要啟動的應用程序,當黑名單應用啟動時,通過動態(tài)劫持的方法將系統(tǒng)屬性文件只讀"ro"方式打開改為讀寫"rw",從而可以通過動態(tài)劫持的方式重置部分代表著設(shè)備不同狀態(tài)(Bootloader是否解鎖、是否運行了可信的Bootloader等)的系統(tǒng)屬性如ro.boot.flash.locked,ro.boot.veritybootstate等,以繞開SafetyNet模塊中的device_state項檢測.
2.2.2Root特征清除
Android的根文件系統(tǒng)如圖4所示,通常Root方案需要修改系統(tǒng)分區(qū)和啟動分區(qū):在系統(tǒng)分區(qū)加入提權(quán)程序su和守護程序daemonsu;修改啟動分區(qū)的初始化腳本init.rc、安全策略sepoicy和安全上下文. 所以為了防止修改系統(tǒng)分區(qū)的Root方案影響SafetyNet的Root檢測,需要清除系統(tǒng)環(huán)境變量下的提權(quán)相關(guān)文件和系統(tǒng)庫文件目錄相關(guān)文件,如系統(tǒng)分區(qū)Shell命令執(zhí)行目錄bin、xbin目的su提權(quán)程序和daemon守護進程、lib目錄的libsupol.so文件、etc目錄下install-recovery腳本以及所有Root權(quán)限管理軟件(如SuperSU)及其配置文件,將設(shè)備的系統(tǒng)分區(qū)恢復為初始狀態(tài).
圖4 Android根文件系統(tǒng)Fig.4 Android root system
2.2.3啟動鏡像重打包
為了掛載鏡像和啟動Hide程序,本文對啟動鏡像內(nèi)Ramdisk分區(qū)的初始化腳本進行了修改,為其增加了提權(quán)鏡像的掛載和隱藏程序,為此需要對啟動鏡像進行重打包,具體過程如下所示.
首先,在恢復模式下提取設(shè)備中的啟動分區(qū)鏡像并備份. 使用"dd"命令拷貝設(shè)備中的塊文件,例如"dd if =/dev/block/mmcblkop19 of=/data/boot.img",將啟動分區(qū)鏡像提權(quán)并存儲到數(shù)據(jù)分區(qū). 然后,使用鏡像處理工具解包鏡像并存放在臨時目錄,修改啟動分區(qū)Ramdisk目錄中的初始化腳本init.rc,即添加新初始化腳本,并在其中設(shè)置post-fs動作列表的觸發(fā)器. post-fs在初始化過程中的設(shè)置系統(tǒng)分區(qū)目錄權(quán)限階段執(zhí)行Shell腳本,配置SEAndroid安全策略文件sepolicy,循環(huán)掛載第一步制作好的提權(quán)鏡像. 再運行Shell腳本將系統(tǒng)分區(qū)中的命令執(zhí)行路徑(/system/bin)軟連接到提權(quán)鏡像掛載到內(nèi)存的執(zhí)行路徑(/sbin),然后以綁定(mount bind)方式掛載文件目錄并設(shè)置權(quán)限,啟動提權(quán)程序的守護進程(daemonsu). 這種方式不需要修改系統(tǒng)分區(qū),通過在初始化階段掛載數(shù)據(jù)分區(qū)的提權(quán)鏡像并初始化,在Android系統(tǒng)中執(zhí)行提權(quán)程序?qū)⒃O(shè)備Root. 隨后啟動上文提到的隱藏Root的程序Hide,其設(shè)置一個黑名單應用的列表屏蔽Root檢測程序,對Zygote要啟動的應用程序進行監(jiān)控. 當啟動黑名單的應用程序時,卸載提權(quán)鏡像并重新讀取設(shè)備的掛載的分區(qū),并調(diào)用Resetprop程序重新設(shè)置設(shè)備的系統(tǒng)屬性值. 在設(shè)備的初始化階段,啟動Hide程序清除提權(quán)程序執(zhí)行的環(huán)境變量,屏蔽黑名單程序,阻止它們使用Shell命令的Root檢測,從而隱藏Root痕跡.
從Google Play中選取了3個利用SafetyNet Root檢測機制進行設(shè)備Root狀態(tài)檢測的知名應用程序Android Pay、Pokemon Go和SafetyNet attest,分別利用傳統(tǒng)的Root方案和本文設(shè)計的Root方案設(shè)計了3組對比性攻擊實驗,實驗結(jié)果表明:雖然SatfetyNet Root檢測可以檢測出傳統(tǒng)的Root方法,但其檢測機制實現(xiàn)中仍存在有較高的安全風險,難以檢測設(shè)計的Root方法.
如表1所示,列出了本文的實驗環(huán)境. 首先,在采用傳統(tǒng)Root方法進行Root的指定手機設(shè)備上,安裝通過SafetyNet Root檢測機制進行Root檢測的應用,查看設(shè)備Root狀態(tài)的檢測效果;然后,在采用本文Root方法進行Root的指定手機設(shè)備上,安裝相同的應用進行測試,查看設(shè)備Root狀態(tài)的檢測結(jié)果.
表1 實驗環(huán)境
從Google Play中選取了3個利用SafetyNet Root檢測機制進行設(shè)備Root狀態(tài)檢測的知名應用程序Android Pay、Pokemon Go和SafetyNet attest,并利用傳統(tǒng)的Root方案和本文設(shè)計的Root方案設(shè)計了3組攻擊性實驗,實驗步驟如下所示.
① 首先解開Nexus 5的Bootloader鎖,裝入定制的第三方恢復系統(tǒng),然后使用此前修改系統(tǒng)分區(qū)的Root方案將設(shè)備Root. 此方案運行在恢復模式下,裝入制作好的安裝包,將所需的Root相關(guān)文件裝入設(shè)備的系統(tǒng)分區(qū)并重打包啟動分區(qū),使之啟動提權(quán)守護程序daemons. Android 5.0之后提權(quán)程序su將應用程序發(fā)出的提權(quán)命令轉(zhuǎn)發(fā)給daemonsu,由daemonsu來執(zhí)行Root權(quán)限的命令,此方案需要將提權(quán)文件su以及提權(quán)守護程序daemonsu裝入系統(tǒng)分區(qū)的命令執(zhí)行目錄(/system/bin). Google移動服務(wù)框架的SafetyNet模塊Root檢測中,su_files和device_state項檢測可以檢測設(shè)備系統(tǒng)分區(qū)的命令執(zhí)行目錄和設(shè)備的系統(tǒng)屬性,因為提權(quán)程序su存放在系統(tǒng)分區(qū)的命令執(zhí)行目錄并且設(shè)備的Bootloader已經(jīng)解鎖以橙色狀態(tài)啟動.
② 繼續(xù)在恢復模式下使用本文改進無需修改系統(tǒng)分區(qū)的Root方案. 如本文2.2節(jié)所述,首先打包并制作提權(quán)鏡像,即將提權(quán)相關(guān)程序(su、daemonsu)、隱藏Root特征程序(Hide)、重置系統(tǒng)屬性程序(Resetprop)等打包為su.img提權(quán)鏡像并存放到數(shù)據(jù)分區(qū). 然后清除掉此前Root方案的痕跡,刪掉系統(tǒng)分區(qū)Root相關(guān)的文件. 緊接著重打包啟動鏡像,使用dd命令提取啟動分區(qū)的鏡像并放在數(shù)據(jù)分區(qū),解壓釋放啟動鏡像,修改其初始化的腳本文件,添加掛載提權(quán)鏡像和Hide隱藏程序的入口點,設(shè)置黑名單的應用列表(默認將谷歌移動服務(wù)加入其中). 最后重新打包啟動分區(qū)的鏡像附上簽名和加密選項,使其啟動加載器中正常啟動,將重打包的鏡像modboot.img,在fastboot模式下刷入設(shè)備中的啟動分區(qū).
③ 設(shè)備啟動后,連接電腦ADB Shell命令,調(diào)用提權(quán)程序su,測試設(shè)備是否被Root. 確定設(shè)備Root后,分別打開應用Android Pay、Pokemon Go和SafetyNet attest判斷設(shè)備的Root狀態(tài).
按照如上步驟,分別利用Android Pay、Pokemon Go和SafetyNet attest對傳統(tǒng)Root方案和本文Root方案進行檢測. 如圖5~7所示,分別是Android Pay、Pokemon Go和SafetyNet attest對傳統(tǒng)Root方案和本文Root方案的檢測結(jié)果.
圖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均成功地檢測出了傳統(tǒng)Root方案對設(shè)備的Root,卻沒有檢測出本文設(shè)計的Root方法對設(shè)備的Root. 實驗結(jié)果表明:雖然SatfetyNet Root檢測可以檢測出傳統(tǒng)的Root方法,但其檢測機制實現(xiàn)中仍存在有較高的安全風險,難以檢測本文設(shè)計的Root方法.
表2 實驗結(jié)果
本文利用本文設(shè)計的Root方案對目前最新版的Android Pay、Pokemon Go和SafetyNet attest等使用了SafetyNet Root檢測機制進行Root檢測的常見應用進行了攻擊實驗,實驗結(jié)果表明:Google平臺級SafetyNet Root檢測機制中存在有繞開風險,難以檢測本文設(shè)計的Root方案.
本文采用逆向工程的方法對Google SafetyNet的Root檢測機制進行了深入分析,并通過攻擊性實驗,實證性地揭示了Google平臺級SafetyNet Root檢測機制中存在有繞開風險,難以檢測本文設(shè)計的Root方案,進而指出當前的SafetyNet Root檢測機制的安全性需要進一步提高. 目前較為理想的緩解策略是將軟件與硬件相結(jié)合,通過硬件保證軟件在安全環(huán)境下,執(zhí)行Root檢測. 但是設(shè)備上硬件檢測機制標準的統(tǒng)一還需要漫長的時間,所以Android設(shè)備上Root檢測機制風險仍有可能長期存在.