前言
首先我们来了解下证书覆盖安装漏洞,此漏洞是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系统修复了此漏洞。
总结
这个漏洞本质上就是劫持的漏洞,只是与常规的劫持有区别,常规的劫持是针对非系统应用,而证书覆盖安装漏洞是针对系统应用的漏洞。在修复后的系统上,如果恶意程序伪装成系统应用,依然可以对证书安装进行覆盖,漏洞仍然存在。