從逆向角度看證書(shū)覆蓋安裝漏洞
前言
首先我們來(lái)了解下證書(shū)覆蓋安裝漏洞,此漏洞是2020年Google官方公布的漏洞之一,其編號(hào)為CVE-2020-0015,這個(gè)漏洞主要形成原因是由于本地安裝證書(shū)被覆蓋所造成的特權(quán)提升漏洞,在文中我會(huì)從逆向的角度去分析證書(shū)的安裝流程,分析漏洞存在的位置以及形成的原因。在分析過(guò)程中會(huì)把代碼進(jìn)行截圖,盡可能覆蓋其中的所有關(guān)鍵代碼,避免本文閱讀者在閱讀過(guò)程中再去反編譯查看代碼。
漏洞介紹
證書(shū)覆蓋安裝漏洞主要體現(xiàn)在統(tǒng)導(dǎo)入證書(shū)的時(shí)候,而導(dǎo)入證書(shū)這部分功能是由系統(tǒng)中的CertInstaller.apk進(jìn)行完成。而在用中并未做安全性校驗(yàn),導(dǎo)致導(dǎo)入證書(shū)界面可以被覆蓋,由于證書(shū)安裝是由系統(tǒng)應(yīng)用完成的,所以當(dāng)安裝界面被覆蓋后,就變向的達(dá)到了特權(quán)提升的目的。根據(jù)官方說(shuō)明該漏洞是存在全系統(tǒng)版本中的,而官方只修復(fù)了Android-8.0、Android-8.1、Android-9、 Android-10系統(tǒng)版本,下面通過(guò)代碼分析此漏洞形成的原因。
分析流程
首先我們通過(guò)ADB命令從手機(jī)中將CertInstaller.apk文件提取到本地,CertInstaller.apk在系統(tǒng)中的/system/app/CertInstaller/目錄下。拿到APK文件后我們?cè)偈褂胘adx、jeb等工具反編譯APK文件。那么我們就通過(guò)ADB命令將/system/app/CertInstaller/CertInstaller.apk拷貝到本地,使用jadx、jeb等反編譯工具將apk反編譯。
首先我們先看下反編譯后的AndroidManifest.xml文件。

從AndroidManifest.xml文件可以看出來(lái)主要入口在CertInstallerMain中,因?yàn)槭莂ctivity,所以我們從onCreate函數(shù)開(kāi)始分析:
代碼位置:com/android/certinstaller/CertInstallerMain.java

從上面代碼中可以看到,首先獲取intent對(duì)象并對(duì)其中攜帶的數(shù)據(jù)進(jìn)行判斷,主要有三種情況:
intent未攜帶任何數(shù)據(jù),或者從sdcard上選擇證書(shū)文件進(jìn)行安裝,也就是在系統(tǒng)設(shè)置中選擇從存儲(chǔ)設(shè)備安裝證書(shū),如下圖:

Intent攜帶了證書(shū)內(nèi)容,就直接創(chuàng)建Intent 啟動(dòng)CertInstaller進(jìn)行證書(shū)安裝,例如:
通過(guò)action 列舉出已安裝的證書(shū)列表
從上面三部分來(lái)看,無(wú)論分析第一種情況還是第二種,都可以到達(dá)證書(shū)安裝的位置,那我們主要分析的是第二種情況, 創(chuàng)建一個(gè)intent用于啟動(dòng)CertInstaller activity,并將攜帶又證書(shū)內(nèi)容得Intent以參數(shù)得形式傳遞過(guò)去。
下面繼續(xù)看:
代碼位置:com/android/certinstaller/CertInstaller.java
從代碼可以看出來(lái)證書(shū)安裝過(guò)程基本都是依賴于CredentialHelper 類完成的,首先掉用createCredentialHelper函數(shù)創(chuàng)建了一個(gè)CredentialHelper 實(shí)例,
從上面代碼可以看出來(lái),在創(chuàng)建CredentialHelper 實(shí)例的同時(shí),還做了證書(shū)解析操作,這里主要看parseCert(byte[] bytes)函數(shù),其中根據(jù)證書(shū)的不同會(huì)將證書(shū)緩存到mCaCerts或mUserCert列表中。然后繼續(xù)分析CertInstaller的OnCreate函數(shù),繼續(xù)往下看對(duì)當(dāng)前的環(huán)境進(jìn)行校驗(yàn),其中keyguardManager.createConfirmDeviceCredentialIntent(null, null); 是檢查是否設(shè)置信任憑證。然后會(huì)調(diào)用到extractPkcs12OrInstall函數(shù)。
PKCS12文件一般由密碼保護(hù),所以需要彈出一個(gè)密碼輸入框,用于輸入密碼。而正常抓包設(shè)置代理證書(shū)或者安裝CA證書(shū)的時(shí)候就會(huì)走到 else里面。繼續(xù)分析代碼。

在這里可以看到InstallOthersAction中的run方法實(shí)際就是調(diào)用的CertInstaller.installOther函數(shù);

這可以看到是安裝證書(shū)之前先做了一個(gè)校驗(yàn),堅(jiān)查是否有CA證書(shū),或者私有與用戶證書(shū),然后會(huì)調(diào)用nameCredential()函數(shù),會(huì)調(diào)用showDialog()彈窗安裝證書(shū)。這也就是漏洞產(chǎn)生的位置。而漏洞形成的原因就是在彈窗的位置因?yàn)闆](méi)有對(duì)系統(tǒng)的dialog彈窗進(jìn)行安全防護(hù),導(dǎo)致dialog可以被劫持覆蓋,這也是該漏洞的主要成因。
漏洞利用
由于設(shè)備環(huán)境原因,只在低版本測(cè)試,未在修復(fù)后的高版本手機(jī)進(jìn)行測(cè)試。
下面看下代碼:
這里為了方便直接創(chuàng)建了一個(gè)線程進(jìn)行監(jiān)聽(tīng),也可以在service中實(shí)現(xiàn)。
為線程創(chuàng)建runnabl任務(wù),判斷當(dāng)前運(yùn)行的應(yīng)用包名,如果為com.android.certinstaller,則啟動(dòng)一個(gè)偽裝好的activity界面進(jìn)行覆蓋,這個(gè)activity界面設(shè)置為dialog顯示。

代碼中g(shù)etTopPackage()是檢測(cè)當(dāng)前運(yùn)行的應(yīng)用程序是哪一個(gè),然后在線程中判斷如果是certInstaller應(yīng)用的進(jìn)程,就進(jìn)行彈窗覆蓋掉系統(tǒng)的dialog,偽造個(gè)假的證書(shū)安裝界面,導(dǎo)致本地特權(quán)提升。

漏洞修復(fù)
下面是Google官方的修復(fù)方式。

從上圖可以看到在CertInstaller.java代碼的onCreate方法中添加了一個(gè)系統(tǒng)屬性” SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS”,添加此屬性的目的就是屏蔽掉其他APP的懸浮窗,避免系統(tǒng)界面惡意程序進(jìn)行覆蓋,這樣就修復(fù)了該漏洞存在的風(fēng)險(xiǎn),目前官方只在Android8-Android10系統(tǒng)修復(fù)了此漏洞。
總結(jié)
這個(gè)漏洞本質(zhì)上就是劫持的漏洞,只是與常規(guī)的劫持有區(qū)別,常規(guī)的劫持是針對(duì)非系統(tǒng)應(yīng)用,而證書(shū)覆蓋安裝漏洞是針對(duì)系統(tǒng)應(yīng)用的漏洞。在修復(fù)后的系統(tǒng)上,如果惡意程序偽裝成系統(tǒng)應(yīng)用,依然可以對(duì)證書(shū)安裝進(jìn)行覆蓋,漏洞仍然存在。