转自CSDN《程序员杂志》 作者:火点,三金
7月30号,新闻又爆出Bluebox安全研究团队发布的安卓新的签名漏洞 “假 ID”,除了最新的4.4版本,几乎所有安卓设备都有此漏洞,本文通过技术分析漏洞细节,发现竟然能制作盗版支付宝,绕开360手机卫士的病毒查杀和支付保镖检测。
7月30号,Bluebox安全研究团队又对外纰漏新的签名漏洞,这是自去年轰动一时的master key漏洞后又一爆点。号称这一漏洞会影响安卓2.1以上系统所有设备,尽管最新的安卓系统4.4或者称KitKat已经修复了这一漏洞,但是仍然有10几亿的安卓设备受到影响。每一个安卓应用程序都有自己的数字签名,本质上来说就是一张ID卡,该漏洞通过伪造假的ID,能模拟Adobe flash插件,注入到浏览器,获得用户在浏览器输入的隐私信息,或者冒充谷歌钱包,然后获得付款和财务数据。
在今年的Blackhat大会上,Bluebox成员Jeff也有一个关于漏洞细节以及如何利用的精彩演讲,有兴趣的读者可以看看相关文档和视频。
Google在4.4上已经打上patch修补了这个签名漏洞,虽然只有短短10几行代码,但是背后的技术原理却极其复杂,请跟我一起来深入剖析,解开安卓签名技术的神秘面纱。
如果你之前对于数字签名不太了解的话,建议先了解PKI,PKCS,RSA,X509等等这些概念。
简单的说,PKI(Public Key Infrastructure)是一套利用公钥加密技术的规范,包括PKCS(Public Key Cryptography Standards)公钥密码系列,X509数字证书系列等等。RSA则是应用最广的公钥加密算法。
X509证书标准用到了公钥加密,引入CA (CertificateAuthority)发证书,作为可信第三方。然而,Google在设计应用签名的时候做了简化,去掉了CA,每个应用变成了自签名,也就是说应用自己生成证书,然后对自己的应用签名,发布出去,在安装应用的时候,系统只是通过安装包证书来验证安装包的完整性,却无法鉴别证书的真假。这个设计留下了巨大的安全设计缺陷,从此安卓的世界里盗版泛滥,广告,病毒,木马可以轻松的通过二次打包嵌入到原本干净的应用中,这个生态圈将永无宁日。
有人会问,这么简单的设计缺陷,Google那帮精英难道不知道吗?当然知道!那为神马要这么做呢?原因很简单,开源,免费。如果引入CA,CA肯定会通过给应用开发者发签名证书收费,除非Google愿意自己搭建免费CA,况且即便是Google搭建了,由于安卓系统可以定制,下游的手机厂商也不一定会买单,不想被Google控制束缚,而且应用市场也不是Google Play这一家,普及起来就更困难了,Google想想还是算了。
回到技术细节,来看看安卓应用签名的实现原理吧。安卓应用APK签名和以前jar签名是一个原理,签名信息会放在META-INF目录下,以RSA签名为例,会生成3个文件。
1 MANIFEST.MF :标准清单文件,记录压缩包的基本信息,其中跟签名相关会记录压缩包中每个文件的hash值,用Base64转码保存如下,称为一个attribute。
Name:res/drawable-xhdpi/ic_launcher.png
SHA1-Digest: h5qMn5HNV9J4T42+TizyymqBqko=
2. CERT.SF:首先记录MANIFEST.MF整个文件的hash,然后是对1中每个attribute分别hash,生成一个entry,用Base64转码保存,和1的attribute很像,但是意义完全不同。有人会问有了MF整个hash,为什么还要对每个attribute再做hash,不是啰嗦,这个问题嘛,其实是早期Jar包为了多次打包多次签名使用的,所以MANIFEST.MF的hash就不一致了,安卓APK不存在这个情况,只是为了兼容标准实现。
SHA1-Digest-Manifest:veBzu0Shy8A3Y9LbBu1pCzVFv8Q=
Name:res/drawable-xhdpi/ic_launcher.png
SHA1-Digest:urIZIkZ1uHqF6dEXkraM62bnjlU=
3. CERT.RSA :这个才是真正的X509证书文件。X509证书有两种格式PEM和DER,两者是等价的,可以通过openssl工具互相转换,CERT.RSA使用的DER格式,这个格式使用的是ASN1编码。巴拉巴拉一堆名词,可能会晕。简单的说CERT.RSA就是一个二进制文件,可以通过工具ASN1 Editor查看编辑。证书里面包含最重要的信息如下:issuer签发者,subject使用者,由于是自签名,所以issuer和subject是相同的。对CERT.SF经过hash和加密后放到signer info中就和原始证书构成了一个带签名信息的证书。
图1-生成带签名信息的证书
常见的签名工具有如下两类:
Oracle Java提供的keytool生成证书, jarsigner签名。
Openssl工具生成证书,Google工具signapk签名。
Android签名的核心过程主要集中在以下几个类和方法中:
PackageParser: collectCertificates() PackageParser: loadCertificates() JarFile: getInputStream() JarVerifier: readCertificates() JarVerifier: verifyCertificate() JarUtil: verifySignature() JarUtil: createChain() JarUtil. findCert() |
核心流程也非常简单:循环遍历APK中的每个文件,第一步验证对应的证书是否正确,证书可能有多个,所以也要循环验证,通过验证证书可以保证CERT.SF中对应在MANIFEST.MF的attribute项是没有篡改的;第二步就是读取这个文件,通过hash对比MANIFEST.MF中的项,保证文件没有篡改。
第一步中验证单个证书是否正确的流程主要是JarVerifier: verifyCertificate()方法中,详细步骤如下:
· 对CERT.SF文件做hash,对证书中signer info的加密数据用public key解密,然后比对比是否相等,调用了JarUtils.verifySignature(),同时也会生成证书链,用于后续返回。
· 第一步验证通过了,说明CERT.SF没有篡改,然后利用CERT.SF去验证整个MANIFEST.MF的hash是否相等,如果不相等,退而求其次验证CERT.SF中的entry和MANIFEST.MF对应attribute的hash是否相等。
· 第二步验证通过,则返回第一步生成的证书链。
细心读者会发现,怎么突然冒出一个证书链的概念?没错,问题就出在这里,Google的数字签名是支持X509的,前面提到CA的概念,如果引入CA,那么证书就会有两级,一个CA,一个自己的,当然实际情况很复杂,会有很多级CA形成树形结构,所以就有证书链的概念,下级的issuer等于上一级的subject,以此类推,直到issuer和subject相等,就是根CA。实际的证书链就会变成如下形式。这个模型的安全关键是上一级证书的issuer会对下一级有一个签名,放到下一级证书的issuer signature节点,从而保证这个证书是我发的,别人没法伪造,在验证证书链的时候,通过上一级的public key对下一级的issuer signature进行解密验证保证合法性。
图2-带证书链的证书
在JarUtils.verifySignature()代码会调用createChain(),代码如下,首先根据证书文件的signer info,找到了第一个证书,然后根据它的issuer遍历找到上级证书,直到找到根证书(issuer和subject相同)。Google解决漏洞的patch关键就在红色标记部分,如果没有这部分代码,建立证书链只要匹配到issuer就可以了,显然通过上面的分析没有验证issuer signature,那么这个证书链就无法保证合法性,攻击者可以简单地通过修改证书的issuer和subject字段很容易伪造一个证书,瞒天过海,通过验证。
实际上安卓APK都是自签名,只会有一个证书,压根就不会有包含多个证书的证书链,但是代码中又支持了X509证书链,像这种旮旯角落的代码,Google肯定没人去测试过,所以这么多年过去了(从android2.1版本开始),漏洞影响的设备就越来越多。
这个漏洞的危害到底有多大呢?这就涉及到android系统对证书签名的验证情况,网上有一篇360安全研究员申迪写的《FakeID签名漏洞分析及利用》很详细的描述了如果利用这个漏洞伪造一个Adobe Flash插件,注入到浏览器,形成攻击。
其核心在以下签名验证代码,浏览器内核Webkit在验证签名的时候只要有一个签名相等,就授权,Bluebox声称的冒充谷歌钱包应该也是类似原理。
安卓系统中还有哪些地方用到签名呢?是不是也有类似的问题呢?
安卓系统用到签名有两个地方,一个是签名权限,一个是share UID。对于权限,大部分安卓开发者都知道,要用的时候在AndroidManifest.xml中申请就可以了,但是很少有人知道权限有个分级属性protection level,有dangerous, normal,signature,system等,平常大家用的大多是dangerous或normal,只要申请了就可以用,但是signature权限,必须要求定义权限的APK和申请权限的APK相同签名。
另外一个是share UID,同样是在AndroidManifest.xml申明,这样就可以在让自己的组件运行在另外一个APK的进程中,如果你申明share UID是system,基本上在系统中可以畅通无阻,但提前也是相同签名。
Google开发framework核心的大牛还是逻辑很清晰的,以上两个功能在比较签名的时候都用到同一个方法: PackageManagerService.compareSignatures(),要求两个APK的签名必须签名个数相同,每个签名也必须相同,这么严格的限制使伪造证书链无法绕开验证,因为证书个数不一样,大牛果然是大牛,能够防患于未然,致敬!
只能说开发Webkit内核的工程师对签名的理解就没有那么深刻,所以才会出现上面的逻辑。
于是我们开始头脑风暴,脱离系统层面,还会有哪些场景用到了签名比较。前面提到了安卓二次打包泛滥,滋生了很多恶意程序,国内各大市场引入了正版验证技术,在正版应用首发的时候通过解析APK中的证书拿到真实自签名证书,后续再有安装包上传市场,就可以通过签名比对确认是否是盗版,360助手,豌豆荚等都有类似功能。
由于移动支付的普及流行,二次打包危害最严重的就是移动金融应用。因此,几乎所有安全软件都退出支付安全功能,其中一个重要的保障就是对盗版金融应用的检查,帮助用户鉴别盗版应用,免收欺诈。
除此之外,一些支付应用自身也会在程序逻辑中做远程签名验证,就是将APK中的签名上传服务器验证,一旦检查不相同,立刻封号。
我们设想上述提到的应用在做正版签名时是否也会犯类似的错误?只验证根证书或者只要匹配一个证书成功就可以了。
于是我们选取支付宝作为测试,被测对象我们选取了360手机卫士,腾讯手机管家和豌豆荚应用市场,攻击步骤如下:
§ 通过以下脚本构造证书链
# need to create folder demoCA, demoCA/certs, demoCA/newcerts under current folder # need to create empty file demoCA/index.txt under current folder # need to create file serial under current folder, then echo 01 to the file openssl genrsa -out ca.key 1024 openssl req -days 3650 -new -x509 -key ca.key -out ca.crt keytool -genkey -alias cert -validity 365 -keyalg RSA -keysize 1024 -keypass 123456 -storepass 123456 -keystore server_keystore keytool -certreq -alias cert -sigalg MD5withRSA -file cert.csr -keypass 123456 -storepass 123456 -keystore server_keystore openssl ca -in cert.csr -out cert.crt -cert ca.crt -keyfile ca.key -notext -config openssl.cnf keytool -import -v -trustcacerts -alias my_ca_root -file ca.crt -storepass 123456 -keystore server_keystore keytool -import -v -alias cert -file cert.crt -storepass 123456 -keystore server_keystore keytool -list -v -keystore server_keystore -storepass 123456 |
jarsigner -keystore server_keystore-storepass 123456 -signedjar alipay_fake.apk alipay.apk cert
图3-系统中真假支付宝签名信息
支付宝 | 8.2.0 |
360手机助手 | 5.2.3 |
腾讯手机管家 | 5.0.0 |
豌豆荚 | 4.13.1 |
图4-盗版支付宝检测结果,从左到右依次是腾讯手机管家,豌豆荚,360手机卫士
最后,我们又做了一个实验,只用一个假证书做成盗版支付宝,结果除了支付宝自身,其他3家都可以检测出来。于是我们断定支付宝没有实现自我签名验证,也就是说如果用户手机上没有装盗版检测的软件,那存在很大的可能性,用户的支付宝账户被盗,财产受损。
我曾在8月7号通过CSDN发过一篇短评,披露360盗版检测的问题,当时使用版本是5.2.3,360手机卫士最新版5.2.5.1038已经能检测盗版的问题,用户升级后可以放心使用。同时我们也希望支付宝作为支付行业龙头,这种基本的自我保护是必须做的,不能完全依赖于第三方软件检测,这也是对用户最基本的责任。