[翻译]一个比Master Key更强大的Android Bug
发表于:
2013-12-5 14:32
15914
[翻译]一个比Master Key更强大的Android Bug
原文:Android Bug Superior to Master Key
出处:http://www.saurik.com/id/18
翻译:Hendy So
今年的早些时候,Bluebox Security宣称发现了一个Android应用程序签名方式的漏洞,它使应用程序的原始开发者放到安装包里的内容(比如代码)可以被修改(这可能导致被恶意程序利用)而不需要校验它的签名。
这个bug在2013黑帽大会上被揭开。由于安全社区对它的大量关注,这个bug很快就在安全圈里以及博客和论坛被找到。现在,这个bug已经完全公开了,CyanongenMod,一个开源的Android ROM,也发布了补丁。
这个bug在媒体上被称为Master Key。因为它可以使你很容易地使用开发者的密钥签名自己的代码。这个bug在LA Times 的TechCrunch 上有完整的介绍。
在我的前一篇文章 中,我对这个bug有详细的文档,并且提供了一个方法来利用此漏洞,可能可以在所有的设备上使用,而且不需要在原始应用程序安装包中满足一些特定的条件(比如需要某些文件)。
当大家都把注意力集中在Master Key时,在同样的地方(Hendy注:指与Master Key漏洞相同的地方,即解析校验apk的地方)AOSP发布了一个补丁 ,显然它可以(与Master Key一样)有类似的利用方法。安卓安全小分队发表了一篇文章 详细解析了这个漏洞。H-Online有相应的英文版本 。
之后就有不少有关这个bug的文章,都在试验这个漏洞技术。结果得出来的结论是这个漏洞比Master Key的破坏力要小一些。因为它需要原始的签名包必须满足一些非常苛刻及不太可能满足的条件。
在本文当中,我会给出利用这个bug的几种不同的技术,并且没有什么限制。实际上,下面讲到的第二个方法甚至比Master Key适用面更广。它可以使任意复杂的安装包从任意已经签名的包中获取签名。最后,我会叙述这个bug的历史,看看它是在什么时候被发现的。
对此技术的具体实现是Cydia Impactor 的一部分,它可以用来在有漏洞的设备获取系统访问权限。Cydia Impactor是我写的一个用来管理手机和破解漏洞的一个工具。它现在可以在破解前自动检测这两个漏洞哪个是用以用的。
负的extra数据
在介绍这个新技术之前,我快速地解释一下现在已有的一个方案,以使得可以更清楚地看到它的局限。这里,我假设大家已经对前面讲到的Master Key漏洞很熟悉了,这个漏洞是基于Android对unzip有多份不同的实现——一份是java实现的,用于检验签名,一份是C++实现的用于解压文件。我前一篇文章详细介绍了这个bug及利用方法。
在这个新的漏洞中,我们来看一下文件的本地文件头(Hendy注:本地文件头是指保存到文件本身的,即文件格式的一部分)——包含文件的meta data(元数据),文件如何存储的信息,文件名以及文件的内容(可能被压缩)。另外,有一个可变长度的额外数据字段以支持文件格式的扩展。
+---------+
| Header |
| Name |
+---------+
| Extra |
+---------+
| Data |
+---------+
在代码的底层实现中的错误在于zip文件中的很多值是以有符号的16位数字来读取的,而不是用无符号的16位数字。由于数字二进制补码 的格式,这会导致大于32767的字段值会变成负数。65535会变成-1。
导致这个问题的原因在于写这段代码的开发者使用了DataInputStream 来解析文件。调用它的readShort方法来获取16位数字。这个方法在java中是返回有符号短整型的。而在java语言中,所有整型都是有符号的。下面是读取extra数据长度的代码:
DataInputStream is = new DataInputStream(rafstrm);
int localExtraLenOrWhatever =
Short.reverseBytes(is.readShort());
(顺便提一下,我通常会对代码版本进行一些修改,比如去掉不相关的逻辑,省略明显的类型转换以及调整代码格式等。我不会改变实际的标识符,那个localExtraLenOrWhatever显然就是Google里面的某位开发者给变量的命名)。
重叠的内容
要计算文件中压缩数据的起始位置,代码将文件头的位置加上文件名的长度以及extra区域的长度。如果extra区域的长度是负数(并不会被检测),那么加法计算(现在变成了减法)的结果就是要读取的数据的位置。
由于偏移量是负数,这会导致偏移回到文件头里面,从而导致与其它数据的冲突。安卓安全小分队文章中提到的攻击方法实际上非常巧妙并且基于一个非常幸运的巧合:在大部分Android安装包里都有的关键文件classes.dex刚好以dex开头(Hendy注:指文件内容以dex三个ASCII字符开头,参看安卓安全小分队的文章)。
这种攻击方法将extra字段的长度设为65533,当java读取后会变成-3。这会使得文件数据(Hendy注:压缩数据)的起始位置从文件名(Hendy注:classes.dex)往回移动三个字符,这正好仍被判断为一个合法的dex文件,因为classes.dex最后三位“dex”正是dex文件的开始三个字节。如果这个文件是stored形式(即未压缩。这是zip文件里文件存在的一种方式,对于已经压缩的文件,通常就是stored方式——Hendy注),那么它就是合法的。
修改后的文件就可以在文件名后面放长度为65533字节的内容了。只要原始文件的长度不大于64K,那么它就可以填充在文件头和修改的数据之间。在下图中,我给出了从C++的视角(正确的情况)和Java视角看到的重叠了的情况:
C++ Header Name 64k Extra Data
+------>+--------->+--------->+-------->
size=10|classes.dex\035\A* ...dex\035\B*
+------>+--------->
Java Header Name <-+
/+-------->
(-3) Extra Data
C++ Header Name 32k Extra Data
+------>+--------->+----------->+-------->
dex\035\A* ...size=10|classes.dex PADDING ... dex\035\B*
+------>+--------->
Java Header Name
<-------------------------------+
+--------> (-32k) Extra
Data
+-> +---------+ ^ ^
| | Detail | | |
| | Local | -/ |
| | Name | |
| | Comment| |
| | Extra | |
| +------------+ |
| +------------+ /
| | Detail | /
| | Local | -/
| | Name |
| | Comment|
| | Extra |
| +------------+
| ...
| +------------+
| | Magic | magic
| | Number | ^
+-- | Start | | find
| Comment | | scan
+------------+ | up
EOF
C++ +-+ Java
+-- |X| --+
| +-+ | (0)
| +-+ <-+
>32k | |/|
| +-+
| PAD
+-> +-+
|\|
+-+
EOF
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!