目前Android MasterKey漏洞被人们熟知的有以下两种:
1、BlueBox Security 在BlackHat 2013大会上提报的Android MasterKey漏洞。(http://blog.csdn.net/androidsecurity/article/details/9280995)
2、安卓安全小分队在BlueBox Security提报漏洞之后发现的的另一Android MasterKey漏洞。(http://blog.csdn.net/androidsecurity/article/details/9984085),但是小分队提出的该漏洞存在一定的局限性,所以理论上可行,但是实际危害并不大。
其实在这之后,Cydia创始人saurik发现了另一个与安卓安全小分队类似的漏洞,saurik发现的漏洞利用方式解决了小分队漏洞的种种限制,漏洞利用细节更加巧妙。可能构造特殊zip包的难度较大,也或者BlueBox Master key的影响还未散去,这个新发现的漏洞虽然比Master key具有更大的隐蔽性,却一直没得到重视。该漏洞的具体细节可参看原文(http://www.saurik.com/id/18)。LBE"免ROOT"即是利用该saurik发现的漏洞。
经过百度安全实验室的分析,该漏洞(编号9695860)的主要原理是,android在解析Zip包时,C代码和Java代码存在不一致性:Java将short整数作为有符号数读取,而C将其作为无符号数。
了解该漏洞的具体细节之前,需先要了解下Zip包的文件结构。(http://www.pkware.com/documents/casestudies/APPNOTE.TXT)
如果一个压缩包里有多个文件,可以认为每个文件都是被单独压缩成一个包,然后再拼成一个大Zip包。下图中的每个FileEntry即代表一个压缩后的文件数据。
为了便于理解,在这里我们通俗地把Zip包文件分为三部分:数据段(FileEntry),索引段(Index)和文件头(Header),Index和Header组合成CentralDirectory(中心目录段)。
1.、文件头在Zip包文件末尾,里面记录了索引段的偏移、大小和索引个数等。
2、 索引段可以认为是一个索引的数组,每个索引记录了其在数据段中对应数据的信息,包括CRC和偏移等。
3.、数据段中的数据分为FileHeader和压缩的文件数据。
4、 FileHeader同样记录了数据的一些基本信息,可以用来跟索引段中记录的数据比较,从而检查压缩包的完整性。
5.、Data区域即是文件被压缩的数据存储区。
通常来说,一般要读取一个Zip压缩包,应分为三步:
1.、先定位到Header,读取索引段的偏移、大小和索引的个数。
2.、定位到索引段,根据每个索引中个字段定义的大小计算偏移,挨个读取索引。
3.、根据索引中记录的数据段的偏移定位到真正的压缩数据。
android校验apk签名是在java层处理,java解析zip的文件格式,读取包里的每个文件,计算哈希进而校验签名。Java在计算zip文件结构偏移时,将整数作为有符号数读取却没有做处理,所以当这些偏移的大小符合一定条件(大于32767)时,java就会出现读取错误的问题。
上图的extraLength,如果大于32767(0×7FFF),java就会将其当成负数。所以java计算下一段数据的偏移时就会出错。小分队提到的方法是,设置数据段中classes.dex所在FileEntry中FileHeader的extraLenth值为0xFFFD,即-3,这样java计算FileEntry中data的起始地址就会往后移3为,正好指向文件名中的”dex”,而”dex”恰好是dex文件头的魔术字,所以可以将这段空间作为正常dex的数据段,而将正确偏移(0xFFFD,即65533)指向的空间写成恶意数据,这样java在校验签名时读取的是正常的dex,而C在加载文件时读取的是恶意的dex文件。
小分队的方法是针对数据段做修改,存在一定缺陷,一是因为无符号short型整数的最大值是64k,所以恶意classes.dex的地址最大只能在FileHeader+64K以外,这其间的空间供正常的classes.dex使用,所以这就要求正常dex的大小不能超过64K,二是因为要求文件名后三位和对应的文件头都是是”dex”,所以这个方法只能伪造dex。
Saruik发现java不仅在处理数据段结构体时存在错误,而且在处理Central Directory时也存在类似错误。所以可以对索引段做手脚,伪造索引。
Java在读取Central Directory时,也是将整数做有符号数处理,不过不同的是如果发现值小于0,就将其忽略,即当0。
所以,只要大于32767的值都会被java当作0。这样我们就可以修改某个index(称作A)的extraLength值大于0×7FFF,然后紧接着在该index写入后为正常的index(称作B),而真正的偏移处写入为恶意的index(称作B’)。然后修改B的extraLength,使得其的大小能够让指针指向下一个index(称作C)是正常的。这样一来,java解析时读到的是ABC,而C读到的是AB’C。前文提到,FileEntry的位置是索引段指向的,所以只要索引被“劫持”,我们就可以zip包的任意位置写入我们的恶意数据,然后让B’指向它即可。
相比Master key,这个漏洞的通用性和危害性更强:
1、不需要apk包里有同名文件,几乎可以往apk里添加任何文件。
2、因为修改的是索引段,通常的压缩软件用的是C读取压缩包的方法,只要恶意索引的名称不太明显,就很难看出该压缩包是非法篡改的,所以隐蔽性很高
3、因为需要解析zip文件结构里特定属性的值才能判断apk是否非法,所以检测的难度也相对更高。