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

從AndroidManifest.xml文件可以看出來主要入口在CertInstallerMain中,因為是activity,所以我們從onCreate函數開始分析:
代碼位置:com/android/certinstaller/CertInstallerMain.java

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

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

在這里可以看到InstallOthersAction中的run方法實際就是調用的CertInstaller.installOther函數;

這可以看到是安裝證書之前先做了一個校驗,堅查是否有CA證書,或者私有與用戶證書,然后會調用nameCredential()函數,會調用showDialog()彈窗安裝證書。這也就是漏洞產生的位置。而漏洞形成的原因就是在彈窗的位置因為沒有對系統的dialog彈窗進行安全防護,導致dialog可以被劫持覆蓋,這也是該漏洞的主要成因。
漏洞利用
由于設備環境原因,只在低版本測試,未在修復后的高版本手機進行測試。
下面看下代碼:
這里為了方便直接創建了一個線程進行監聽,也可以在service中實現。
為線程創建runnabl任務,判斷當前運行的應用包名,如果為com.android.certinstaller,則啟動一個偽裝好的activity界面進行覆蓋,這個activity界面設置為dialog顯示。

代碼中getTopPackage()是檢測當前運行的應用程序是哪一個,然后在線程中判斷如果是certInstaller應用的進程,就進行彈窗覆蓋掉系統的dialog,偽造個假的證書安裝界面,導致本地特權提升。

漏洞修復
下面是Google官方的修復方式。

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