您当前的位置:安全博客 > 安全漏洞 > Shadows Everywhere漏洞浅析,影响全部安卓用户

Shadows Everywhere漏洞浅析,影响全部安卓用户

阿里无线安全团队发表于2015年03月24日
      Shadows Everywhere顾名思义,这是一个存在在所有安卓设备中的一个影子漏洞,利用它能够攻击任何APK,并且不改变其证书签名,进而能够进行正常传播和升级替代原官方原始应用。利用这个漏洞能够轻易绕过安卓的证书验证机制,进而实施攻击。

Android证书验证机制

      首先,详细说明Android应用程序安装文件APK的证书验证机制。

1. 基本算法

      (1)消息摘要 -Message Digest
        简称摘要,在消息数据上,执行一个单向的Hash函数,生成一个固定长度的Hash值,也称为数字指纹,消息摘要有以下特点:
        1. 通过摘要无法推算得出消息本身。
        2. 如果修改了消息,那么摘要一定会变化(除开碰撞情况)。
        3. 消息摘要只能保证消息的完整性,并不能保证消息的不可篡改性。
        在Android证书里,一般采用SHA-1算法来生成摘要值。
      (2)数字签名 - Signature
        消息的发送者用自己的私钥对消息摘要进行加密,产生一个加密后的字符串,称为数字签名。因为发送者的私钥保密,可以确保别人无法伪造生成数字签名,也是对信息的发送者发送信息真实性的一个有效证明。数字签名是 非对称密钥加密技术 + 数字摘要技术 的结合。
        数字签名技术是将信息摘要用发送者的私钥加密,与原文一起传送给接收者。接收者只有用发送者的公钥才能解密被加密的信息摘要,然后接收者用相同的Hash函数对收到的原文产生一个信息摘要,与解密的信息摘要做比对。如果相同,则说明收到的信息是完整的,并且是由该发送者生成的(因为只有发送者才拥有对应的私钥)。
      (3)数字证书 - Certificate
        数字证书是一个经证书授权中心数字签名的包含公开密钥拥有者信息以及公开密钥的文件。
        证书包含的有效信息有:证书版本、证书序号、证书颁发机构、证书有效期、证书拥有者和拥有者公钥,以及证书颁发机构对该证书的签名。

        需要注意的是Android APK中的CERT.RSA证书可以是自签名的,并不需要这个证书是第三方权威机构发布或者认证的,用户可以在本地机器自行生成这个自签名证书。 

      当使用某证书时,需要验证该证书的合法性:使用证书颁发者的公钥,然后对整个证书(除了证书数字签名以外)采用给定的证书签名算法计算,然后将计算结果同证书数字签名对比,相同则可以证明该证书确实由证书颁发者颁发的合法证书;不同则说明有篡改存在,证书不合法。

2. Android证书组成

      APK文件实际上是一个压缩文件包,我们将文件后缀.APK修改为.zip,可以使用解压缩软件直接打开。如Twitter v5.21.1.APK:

      证书验证相关的文件都放在META-INF\ 文件夹中:

     (1) MANIFEST.MF
      遍历APK包中除了META-INF\ 文件夹以外的所有文件,利用SHA1算法生成这些文件的消息摘要,然后转化为对应的base64编码。MANIFEST.MF存储的是文件的摘要值,保证完整性,防止文件被篡改。Twitter 的MANIFEST.MF:

Manifest-Version: 1.0
Built-By: 0.12.2
Created-By: Android Gradle 0.12.2

Name: res/drawable-xhdpi-v4/ic_compose_action_spen_active_default.png
SHA1-Digest: kb5uDh5hpAO47dwkbX1ieG0ti0A=

Name: res/drawable/em_1f600.png
SHA1-Digest: O3xdVgJVGswlakApdQgFnitoOzg=
……
     (2) .SF
      xx.SF文件(xx为使用者证书的自定义别名,默认为CERT,即CERT.SF),保存的是MANIFEST.MF的摘要值, 以及MANIFEST.MF中每一个摘要项的摘要值,然后转化成对应的base64编码。虽然该文件的后缀名.sf(SignatureFile)看起来是签名文件,但是并没有私钥参与运算,也不保存任何签名内容。
      Twitter 的COM.SF:

Signature-Version: 1.0
Created-By: 1.0 (Android)
SHA1-Digest-Manifest: Gt7RGVT9TNTiDIuVIr5YNcT6gm8=

Name: res/drawable-xhdpi-v4/ic_compose_action_spen_active_default.png
SHA1-Digest: Ajql5MMIUV8fqjW6yk92/KiBh0M=

Name: res/drawable/em_1f600.png
SHA1-Digest: /U8jvAiHiO7AyAxf1Rkoex0IvHI=

……
      (3) .RSA / .DSA
      .RSA / .DSA文件(后缀不同采用的签名算法不同,.RSA使用的是RSA算法, .DSA使用的是数字签名算法DSA,目前APK主要使用的是这两种算法),保存的是第二项.SF文件的数字签名,同时还会包括签名采用的数字证书(公钥)。特别说明,当使用多重证书签名时,每一个.sf文件必须有一个.RSA/.DSA文件与之对应,也就是说使用证书CERT1签名时有CERT1.SF和CERT1.RSA,同时采用证书CERT2签名时又会生成CERT2.SF和CERT2.RSA。
     Twitter 的COM.RSA(解码后):

Certificate:
    	Data:
        		Version: 3 (0x2)
        	Serial Number: 1272409294 (0x4bd76cce)
    	Signature Algorithm: sha1WithRSAEncryption
        	Issuer: C=US, ST=CA, L=San Francisco, O=Twitter, Inc., OU=Mobile, CN=Leland Rechis
        	Validity
         		Not Before: Apr 27 23:01:34 2010 GMT
          	Not After : Aug 25 23:01:34 2048 GMT
        	Subject: C=US, ST=CA, L=San Francisco, O=Twitter, Inc., OU=Mobile, CN=Leland Rechis
        	Subject Public Key Info:
         		Public Key Algorithm: rsaEncryption
                		Public-Key: (1024 bit)
                		Modulus:
                    …
                	Exponent: 65537 (0x10001)
    	Signature Algorithm: sha1WithRSAEncryption
         …
.SF Signature Algorithm: sha1WithRSAEncryption
	……
      .RSA文件包括证书和.SF签名2个部分,其中证书里面也有一个签名,是证书颁发者对该证书的签名。
       对APK的证书签名具体原理和算法,可以参考
      https://Android.googlesource.com/platform/build/+/jb-mr1-dev/tools/signAPK/SignAPK.java

3. Android证书验证机制

      Android 系统不允许安装没有任何数字签名的应用APK程序,所有应用程序必须使用某个证书进行签名(一般为应用开发者自签名证书):
      APK源文件,首先由应用开发者使用自己的私钥,对整个文件进行签名,生成上述的三个文件,然后打包成签名后的APK文件;然后发布到市场,图示是Google市场。
      用户从市场下载APK安装文件,在真正安装APK前,会首先验证数字签名。具体步骤:
      (1) 首先计算除META-INF\ 文件夹以外所有文件的SHA1摘要值,同MANIFEST.MF文件中的摘要值做比对。如果不同,则证明源文件被篡改,验证不通过,拒绝安装。
      (2) 计算MANIFEST.MF的摘要值, 以及MANIFEST.MF中每一个摘要项的摘要值,同.SF文件中的摘要值做比对。如果不同,则证明.SF被篡改,验证不通过,拒绝安装。
      (3) 从.RSA 文件中取出开发者证书,然后从证书中提取开发者公钥,用该公钥对.SF文件做数字签名,并将结果同.RSA文件中的.SF签名进行比对。如果不同,则验证不通过,拒绝安装。

存在的问题

      2014年报出来的Fake ID漏洞,是对证书链的验证方法存在漏洞,没有对证书本身的签名进行验证(不同于.SF签名),场景:证书颁发者A对证书B进行签名,然后使用证书B对APK的.SF文件签名,而安装APK时仅仅验证了证书B对.SF文件的数字签名,而没有验证颁发者A对该证书B本身的数字签名。目前,GOOGLE已经对该漏洞进行了修复,但笔者认为,该修复并不完全:仅仅修复了证书链的认证方法,没有对自签名证书中的证书数字签名进行验证,存在着一定的问题。
      代码段org.apache.harmony.security.utils. JarUtils:

191     // Signer is self-signed
192     if (signer.getSubjectDN().equals(signer.getIssuerDN())){
193         return (X509Certificate[])chain.toArray(new X509Certificate[1]);
194     }
      当采用自签名证书时,Android直接跳过验证,将证书加入了合法证书列表。这导致攻击者拿到.RSA / .DSA文件后,可以对其中的证书(除了证书拥有者公钥之外)随意篡改,包括证书序号、证书有效期、证书拥有者名字等等。而Android在安装APK时,仅仅使用了证书中的公钥,其它信息未使用也未作任何验证,只要使用公钥验证.SF文件的数字签名通过就可以正常安装使用。

1. 证书有效期篡改

      以Twitter的CERT.RSA证书为例,直接修改证书有效期(仅仅改动COM.RSA文件):
Certificate:
    ……
        Validity
            Not Before: Jan 19 14:39:32 2011 GMT
            Not After : Jan 11 14:39:32 2011 GMT
……
      很明显的证书有效期错误,结束时间比开始时间还早,但是修改后的APK依然可以正常安装、运行,因为CERT.RSA文件里的公钥(证书的一部分)和.SF文件都没有修改,可以通过Android证书验证机制。
      联想开来,某个已过期的证书依然可以使用,假如某个拥有高权限的证书已经过期,危害会很大。例如Adobe颁发过某个证书C(使用Adobe公钥签名该证书),然后证书C过期了,但是由于用Adobe的公钥验证该证书仍然可以通过(修复过的证书链认证可以通过,除非Adobe吊销它自己的证书),而Android并没有验证证书有效性,所以用证书C签过名的APK依然拥有Adobe的高权限。

2. 证书拥有者主体篡改

      以Twitter的COM.RSA证书为例,直接修改证书拥有者名字(仅仅改动COM.RSA文件):

Certificate:
    ……
        Subject: C=US, ST=CA, L=San Francisco, O=abcdefg., OU=Mobile, CN=Leland Rechis
……
      将开发者信息修改为了abcdefg,但是修改后的APK依然可以正常安装、运行,因为CERT.RSA文件里的公钥(证书的一部分)和.SF文件都没有修改,可以通过Android证书验证机制。
     联想开来,开发者主体信息很容易修改,造成知识版权问题,例如可以把Google开发的APK的开发者证书信息修改成我自己的名字,扩大影响力;或者,把自己开发的恶意APK冠上Google的名字。

3. 证书吊销CRL

      Android证书机制并没有引入CRL吊销机制,已经吊销但拥有合法签名的证书依然可以使用,和第一项证书过期类似(证书过期,也是吊销的一种方法)。
      但是,证书吊销还有另外一个应用,当发现某个证书的私钥有泄露或是被破解时,业界常用的手法是吊销该证书,加入CRL列表,例如2012年发生的Yahoo Axis私钥泄露事件。在Android系统里,并没有采用证书吊销机制,设想场景:Adobe颁发过某个证书D(使用Adobe公钥签名该证书),然后证书D在使用过程中发生了私钥泄露,那么此时只有吊销Adobe自己的根证书才能真正使证书D失效,成本大而且会影响其他Adobe签过名的证书。


除了以上这些,Shadow Everywhere的更多细节,更多利用方式,将于今年 6.16-6.17 blackhat 移动安全峰会上进行公布。会议之后我们也将更新漏洞解析,更多内容,敬请期待!


安全漏洞