Android 安全开发之 ZIP 文件目录遍历

一、ZIP文件目录遍历简介

由于ZIP压缩包文件中容许存在“../”的字符串,攻击者能够利用多个“../”在解压时改变ZIP包中某个文件的存放位置,覆盖掉应用原有的文件。若是被覆盖掉的文件是动态连接so、dex或者odex文件,轻则产生本地拒绝服务漏洞,影响应用的可用性,重则可能形成任意代码执行漏洞,危害用户的设备安全和信息安全。好比近段时间发现的“寄生兽”漏洞、海豚浏览器远程命令执行漏洞、三星默认输入法远程代码执行漏洞等都与ZIP文件目录遍历有关。html

阿里聚安全的应用漏洞扫描服务,能够检测出应用的ZIP文件目录遍历风险。另外咱们发现日本计算机应急响应小组(JPCERT)给出的修复方案存在缺陷。若是使用不当(它提供的示例文档就使用错误),可能起不到防止ZIP文件目录遍历的做用,而且国内有修复方案参考了此方案。java

二、漏洞原理和风险示例

2.1 漏洞原理

在Linux/Unix系统中“../”表明的是向上级目录跳转,有些程序在当前工做目录中处理到诸如用“../../../../../../../../../../../etc/hosts”表示的文件,会跳转出当前工做目录,跳转到到其余目录中。 android

Java代码在解压ZIP文件时,会使用到ZipEntry类的getName()方法,若是ZIP文件中包含“../”的字符串,该方法返回值里面原样返回,若是没有过滤掉getName()返回值中的“../”字符串,继续解压缩操做,就会在其余目录中建立解压的文件。segmentfault

如咱们构造的ZIP文件中有以下文件:浏览器

进行解压的代码以下,没有对getName进行过滤:安全

解压操做时的日志:服务器

此ZIP文件存放在SD卡中,想让解压出来的全部文件也存在SD卡中,可是a_poc.txt文件却存在了应用的数据目录中:网络

2.2 风险示例

以海豚浏览器远程代码执行漏洞为例。 oracle

海豚浏览器的主题设置中容许用户经过网络下载新的主题进行更换,主题文件实际上是一个ZIP压缩文件。经过中间人攻击的方法能够替换掉这个ZIP文件。替换后的ZIP文件中有从新编译过的libdolphin.so。此so文件重写了JNI_OnLoad()函数:app

此so文件以“../../../../../../../../../../data/data/mobi.mgeek.TunnyBrowser/files/libdolphin.so”的形式存在恶意ZIP文件中。海豚浏览器解压恶意ZIP文件后,从新的libdolphin.so就会覆盖掉原有的so文件,从新运行海豚浏览器会弹出Toast提示框:

能弹出Toast说明也就能够执行其余代码。

这里分析下此漏洞产生的缘由是:

一、主题文件实际上是一个ZIP压缩包,从服务器下载后进行解压,可是解压时没有过滤getName()返回的字符串中是否有“../”:

二、动态连接库文件libdolphin.so,并无放在应用数据的lib目录下,而是放在了files目录中:

加载使用的地方是com.dolphin.browser.search.redirect包中的SearchRedirector:

应用使用的是System.load()来加载libdolphin.so而非System.loadLibrary(),在Android中,System.loadLibrary()是从应用的lib目录中加载.so文件,而System.load()是用某个.so文件的绝对路径加载,这个.so文件能够不在应用的lib目录中,能够在SD卡中,或者在应用的files目录中,只要应用有读的权限目录中便可。

在files目录中,应用具备写入权限,经过网络中间人攻击,同时利用ZIP文件目录遍历漏洞,替换掉文件libdolphin.so,达到远程命令执行的目的。

应用的lib目录是软连接到了/data/app-lib/应用目录,若是libdolphin.so文件在lib目录下就不会被覆盖了,第三方应用在执行时没有写入/data/app-lib目录的权限:

三、JPCERT修复方案的研究

在研究中咱们发现JPCERT提供的修复方案存在缺陷。它是利用Java的File类提供的getCanonicalPath()方法过滤掉zipEntry.getName()返回的字符串中所包含的“../”,而后检查这个字符串是不是以要解压到的目标目录字符串为开头,若是是,返回getCanonicalPath()获取到的字符串,若是不是,则抛出异常:

可是在JPCERT给出的示例代码中,对validateFilename()的调用对于APP来讲不会达到防止任意目录遍历的目的:

其使用“.”,做为要解压到的目的目录,“.”表示当前目录,经测试APP进程的当前工做目录是根目录“/”:

查看进程状态,获得的APP进程的当前工做目录cwd是连接到了根目录:

以下的Demo,若是采用JPCERT示例中validateFilename(entry.genName(), “.”)的调用方式,仍是会产生目录遍历读到系统配置文件:

读到的hosts文件内容:

正确的调用validateFilename()形式是传入的要解压到的目的目录不要用“.”,而是指定一个绝对路径。

四、阿里聚安全对开发者建议

  1. 对重要的ZIP压缩包文件进行数字签名校验,校验经过才进行解压。

  2. 检查Zip压缩包中使用ZipEntry.getName()获取的文件名中是否包含”../”或者”..”,检查”../”的时候没必要进行URI Decode(以防经过URI编码”..%2F”来进行绕过),测试发现ZipEntry.getName()对于Zip包中有“..%2F”的文件路径不会进行处理。

  3. 在应用上线前使用阿里聚安全的安全扫描服务,尽早发现应用的安全风险。

阿里聚安全扫描器建议修复方案:在使用java.util.zip包中ZipInputStream类的进行解压操做时,进行检查。示例以下

也可使用java.util.zip包中的ZipFile类,直接读取Zip包中的全部entries,而后检查getName()的返回值是否包含“../”:

五、参考

[1] https://www.jpcert.or.jp/present/2014/20140910android-sc.pdf
2] [《海豚浏览器与水星浏览器远程代码执行漏洞详解》
3] [《影响数千万APP的安卓APP“寄生兽”漏洞技术分析》
4] [《三星默认输入法远程代码执行》
[5] http://www.oracle.com/technetwork/articles/java/compress-1565076.html
[6] http://stackoverflow.com/questions/1099300/whats-the-difference-between-getpath-getabsolutepath-and-getcanonicalpath
[7] http://stackoverflow.com/questions/7016391/difference-between-system-load-and-system-loadlibrary-in-java




相关文章
相关标签/搜索