一.关于app的缓存代码

安卓的应用程序apk文件是zip压缩格式的文件,apk文件中包含的classes.dex文件相当于app的可执行文件,当app运行后系统会对classes.dex进行优化,生成对应的odex格式的文件

odex文件相当于app的可执行文件的缓存代码,一般安卓系统在第一次加载运行apk时会将系统生成odex文件存放于/data/dalvik-cache目录下

如图:

可以看到该目录下的文件只有system用户有写权限,只有在拥有system权限的情况下才能对odex文件进行写操作。

二.广泛流行的插件机制

由于安卓应用的升级都需要重新安装程序,频繁的升级给用户体验和开发都带来了不便,所以市面上的app都开始采用插件机制,利用插件机制可以做到无缝升级和扩展功能,app只需要引入相应的插件文件就可以做到功能添加或升级,无需再重新安装程序。

app插件机制的实现方式是把相关功能编写成单独的apk或jar文件,然后在程序运行时用DexClassLoader动态加载,进行反射调用。我们来看一下DexClassLoder的定义

public DexClassLoader (String dexPath, String optimizedDirectory, String libraryPath, ClassLoader parent)
dexPath:是要加载的jar/apk的路径
optimizedDirectory:目标odex的路径
libraryPath:依赖的native library(so文件)路径
parent:父加载器

下面是常见的调用DexClassLoader的代码片段

String dexPath = Environment.getExternalStorageDirectory() + File.separator +  "output.jar";
File optimizedDir = this.getCacheDir();
DexClassLoader dexClassLoader = new DexClassLoader(dexPath, optimizedDir.getAbsolutePath(), null, this.getClassLoader());

如下图所示,drozer.apk插件在被调用后生成了drozer.dex缓存文件,注意dex这个文件是odex格式,且这个目录是app的私有目录。

三. 插件机制引入的攻击点

在2013年,国外的mwr实验室给出了一个利用中间人的方式劫持app升级插件的攻击案例,参考

https://labs.mwrinfosecurity.com/blog/2013/11/20/applovin-ad-library-sdk-remote-command-execution-via-update-mechanism/

几年前,大部分采用插件机制的app,在载入插件前都没有对插件文件进行完整性校验,导致黑客可以通过中间人劫持的方式替换app的升级插件,在插件中嵌入恶意代码控制用户的app和手机。

现今,大部分采用插件机制的app都加强了安全性,如最早使用插件开发方式的微信等app,在下载使用插件前都会校验插件文件的签名,黑客已经无法通过中间人的方式替换插件攻击app。

四. 插件机制新的攻击点

近日,国外的nowsecure公司公布了三星输入法的一个漏洞,利用过程直接替换了系统app的odex缓存代码。参考:https://www.nowsecure.com/blog/2015/06/16/remote-code-execution-as-system-user-on-samsung-phones/

三星输入法是拥有系统最高级别的 system 权限,可以直接替换任意app的缓存文件。那安卓app插件的缓存代码是否和APP主程序直接产生的缓存代码一样能被任意替换?

我们去android源码中验证了一下,通过DexClassLoader() 加载jar/apk文件,最终会通过native接口openDexFileNative() 进入到native层。

对应于android-4.2.2_r1/dalvik/vm/native/dalvik_system_DexFile.cpp中的Dalvik_dalvik_system_DexFile_openDexFileNative() 方法,在native层对几个参数做一系列校验,如果检测到第二个参数指定的odex文件存在,则会调用dvmOpenCachedDexFile() 直接打开,调用处的代码如下:

 fd = dvmOpenCachedDexFile(fileName, cachedName,
dexGetZipEntryModTime(&archive, entry),
dexGetZipEntryCrc32(&archive, entry),
isBootstrap, &newFile, /*createIfMissing=*/true);

很明显,第3、4个参数对应的是优化前的classes.dex的时间戳和crc校验值。最终会调用

dvmCheckOptHeaderAndDependencies(fd, true, modWhen, crc, expectVerify, expectOpt)

如果crc、modWhen参数正确,则返回该odex的文件句柄;若crc、modEWhen校验错误,则尝试删除错误的odex,并重建新的odex。所以,攻击者如果要注入目标odex,需要对修改后的odex文件的crc及modWhen做修改

下面是一个修改后的odex文件实例,dex_old是修改前的odex文件,dex_new是修改后的dex文件,两个文件的md5不一样,但是crc及modWhen却是一样的,这样就可以绕过DexClassLoader的校验

五. “寄生兽”漏洞的真正危害

安卓应用的代码缓存机制是程序在执行时优先加载运行缓存代码,而google却只对缓存代码做了可以伪造的弱校验,这明显是一个安全架构实现上的严重漏洞。

广大app开发者在使用插件机制开发app时可以对插件文件做完整性校验,而系统生成的缓存代码却无法做到有效保护,一旦攻击者将恶意代码注入到缓存代码中,开发者对app插件文件做的各种保护都将失效。这种攻击很难被发现,即使关机后重启,只要app一运行,恶意代码也会随之运行,同时安全软件对这一块的检查和防御也几乎为零。

六.现实中的“寄生兽”漏洞攻击案例

(1)利用zip解压缩漏洞覆盖缓存代码

在三星输入法漏洞的利用中,作者用到了安卓下的zip解压缩漏洞,这个漏洞是单独的一个漏洞,且由来以久,在google官方的文档中已经做了警告,存在问题的是ZipEntry.getName()方法,我们看一下google文档中对该函数的描述:

链接:http://developer.android.com/reference/java/util/zip/ZipEntry.html#getName()

Gets the name of this ZipEntry

Security note: Entry names can represent relative paths. foo/../bar or ../bar/baz ,

for example. If the entry name is being used to construct a filename or as a path component, it must be validated or sanitized to ensure that files are not written outside of the intended destination directory.

可以看到google对该方法给出了一个安全提示,提示开发者如果该方法的返回值中包含有”../”跳转符,需要特别注意不要将文件写到了目标文件夹之外。如果不对”../”跳转符做过滤,就有可能遍历目录,在解压zip文件时以本app的权限覆盖任意文件。

下面是一个安卓zip解压缩的常用代码片段:

 ZipFile zip = new ZipFile(zipFile);
for(Enumeration entries = zip.entries();entries.hasMoreElements();){
ZipEntry zipEntry = (ZipEntry)entries.nextElement();
File file = new File(outputDirectory + File.separator+ zipEntry.getName());

}

如果没有对 zipEntry.getName进行检查,盲目解压创建文件,将会穿越目录建立文件,如图:

我们检测后发现市面上几乎所有使用zip解压缩功能的app都存在漏洞,为“寄生兽”漏洞的攻击提供了便利,主要分为三类情况:

APP关联文件类

这类漏洞主要影响有皮肤功能的APP,如输入法,浏览器类APP .很多app在manifest中做了zip类文件的关联,如果注册的文件格式被点击,对应的app就会启动解压文件。下图是app注册文件关联的一个示例

 <intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="*/*"></data>
<data android:pathPattern=".*\\.ssf"></data>
</intent-filter>

这段xml就把app同ssf格式的文件进行关联,其实这个文件的格式是zip压缩格式,用户在手机中下载打开ssf文件时,就会启动对应的app自动解压文件,文件中包含的恶意代码可以覆盖该app的缓存代码

验证某输入法app漏洞视频

APP自升级类

这类漏洞主要影响有自动升级下载zip类文件功能的app,在app下载文件过程中可以被中间人劫持攻击,我们发现地图类的app和sdk插件最容易收到攻击,app在下载解压资源文件的过程中被攻击

验证某地图app漏洞视频

APP默认解压类

这类漏洞主要影响默认有解压缩zip文件功能的app,如浏览器直接下载zip文件打开后,app就被感染缓存代码。

验证某浏览器漏洞视频:

(2)利用adb backup覆盖缓存代码

如果开发者没有在manifest里指定allowBackup="false" ,就可以在不需要root权限的情况下备份、恢复app私有目录下的数据。如果该app用到了插件机制,则对应的插件的odex文件也会被备份。攻击者可以先用adb backup备份用户数据,对备份下来的odex文件进行修改,然后用adb restore恢复回去,就可以替换掉正常的odex文件,造成代码劫持。

(3)其他可能的APP数据读写

如果一个木马病毒利用root权限实施“寄生兽”漏洞攻击方式,将能实现隐蔽的apt木马攻击方式,长期潜伏在用户的手机类,安全软件很难发现app的缓存代码被感染了。

七. “寄生兽”漏洞的防护方案

“寄生兽”漏洞的核心有两点,一是google没有考虑odex的安全问题需要开发者自己做防护,另一个是要阻断漏洞的攻击入口和利用方式,这里我们给出一些防护建议缓解该漏洞的攻击。

(1)对odex文件进行完整性校验

由于对odex一般是由系统(DexClassLoader)自动生成的,且odex与apk/jar是相对独立的,开发者事先无法知道odex文件的MD5等信息,所以很难通过MD5校验等手段保护odex的完整性;同时,系统的DexClassLoader函数只是校验了odex中的crc、modWhen字段,可以很轻易的被绕过。

所以,目前对odex的防护只能由app自身来做,可以在每次运行DexClassLoader之前,清除已经存在的odex

另外,在odex第一次生成之后,存储odex文件的MD5值,以后每次调用DexClassLoader的时候都对odex文件进行MD5校验

(2)对可能的劫持odex的攻击入口漏洞进行修复

对zip解压缩的漏洞,只需要在调用zipEntry.getName()的时候,过滤返回值中的”../”跳转符

对于引用的第三方的zip库也需要注意,可以用上面的测试用例测试一下第三方库是否有zip解压缩的漏洞;

调用DexClassLoader动态加载dex的时候,第二个参数不要指定在sdcard上;

在manifest里指定allowBackup=”false”,防止应用数据备份覆盖。

本文转自:http://appscan.360.cn/blog/?p=159

附:生成Android zip解压缩目录穿越漏洞apk的代码

import zipfile
import sys if __name__ == "__main__":
try:
with open("test.txt", "r") as f:
binary = f.read()
zipFile = zipfile.ZipFile("test.zip", "a", zipfile.ZIP_DEFLATED)
info = zipfile.ZipInfo("test.zip")
zipFile.writestr("../../../../../data/data/com.corp.demo/files/test.txt", binary)
zipFile.close()
except IOError as e:
raise e

Android“寄生兽”漏洞技术分析的更多相关文章

  1. 美链BEC合约漏洞技术分析

    这两天币圈链圈被美链BEC智能合约的漏洞导致代币价值几乎归零的事件刷遍朋友圈.这篇文章就来分析下BEC智能合约的漏洞 漏洞攻击交易 我们先来还原下攻击交易,这个交易可以在这个链接查询到. 我截图给大家 ...

  2. Ripple 20:Treck TCP/IP协议漏洞技术分析

    本文由“合天智汇”公众号首发,作者:b1ngo Ripple 20:Treck TCP/IP协议漏洞技术分析 Ripple20是一系列影响数亿台设备的0day(19个),是JSOF研究实验室在Trec ...

  3. Android埋点技术分析

    1.现有的几种埋点技术的实现原理和优劣分析 (1)代码埋点:将收集数据的代码直接写在需要的地方,当用户点击某个控件或者打开某个页面时调用到该部分代码完成数据的收集. 优势:准确性高,收集数据和发送数据 ...

  4. Android BroadcastAnyWhere(Google Bug 17356824)漏洞具体分析

    Android BroadcastAnyWhere(Google Bug 17356824)漏洞具体分析 作者:简行(又名 低端码农) 继上次Android的LaunchAnyWhere组件安全漏洞后 ...

  5. android添加账户流程分析涉及漏洞修复

    android修复了添加账户代码中的2处bug,retme取了很酷炫的名字launchAnyWhere.broadAnywhere(参考资料1.2).本文顺着前辈的思路学习bug的原理和利用思路. 我 ...

  6. Android 热修复技术(1)---原理

    热修复技术分为几部分: 原理介绍 Android HotFix源码分析 自定义框架 1.Android分包MultiDex原理 首先Dex是什么东西? Dex就是Window里面的exe文件 也就是可 ...

  7. 【转】Android 防破解技术简介

    http://www.cnblogs.com/likeandroid/p/4888808.html Android 防破解技术简介 这几年随着互联网的不断发展,Android App 也越来越多!但是 ...

  8. 《Android系统源代码情景分析》连载回忆录:灵感之源

    上个月,在花了一年半时间之后,写了55篇文章,分析完成了Chromium在Android上的实现,以及Android基于Chromium实现的WebView.学到了很多东西,不过也挺累的,平均不到两个 ...

  9. 全面了解Android热修复技术

    WeTest 导读 本文探讨了Android热修复技术的发展脉络,现状及其未来. 热修复技术概述 热修复技术在近年来飞速发展,尤其是在InstantRun方案推出之后,各种热修复技术竞相涌现.国内大部 ...

随机推荐

  1. Linux sudo 命令使用简介

    Linux sudo 命令使用简介 by:授客 QQ:1033553122 基本语法: $ sudo [-u username] [command] -u:将身份变成username的身份 #编辑/e ...

  2. java EE 监听器

    生命周期监听器与属性改变监听器都必须使用@WebListener或在web.xml中声明,容器才会知道要加载.读取相关的监听器.

  3. springcloud 入门 5 (feign源码分析)

    feign:(推荐使用) Feign是受到Retrofit,JAXRS-2.0和WebSocket的影响,它是一个jav的到http客户端绑定的开源项目. Feign的主要目标是将Java Http ...

  4. 6.1 函数的返回值、匿名函数lambda、filter函数、map函数、reduce函数

      函数的返回值: 函数一旦执行到   return,函数就会结束,并会返回return 后面的值,如果不使用显式使用return返回,会默认返回None . return None可以简写为   r ...

  5. She Left Her Shoes

    She left her shoes, she took everything else, her toothbrush, her clothes, and even that stupid litt ...

  6. centos6.5安装mysql

    1.yum -install  mysql mysql-server -y 2.修改mysql的root的密码 登录:mysql -uroot        修改密码:            use ...

  7. sql server 用户'sa'登录失败(错误18456)

    转载于:http://thenear.blog.51cto.com/4686262/865544 用户'sa'登录失败(错误18456)解决方案图解     当我们在使用sql server 的时候可 ...

  8. SQL Server登录方式

    SQL Server登录服务器有两种验证方式,一种是windows身份验证,也就是本机验证,另一种就是SQL Server验证,就是使用账号密码的方式验证. 在使用windows身份验证登录时,直接就 ...

  9. round()和trunc()用法

    round(数字 | 列 保留小数的位数):四舍五入. select a.*,round(s),round(-s) from bqh4 a trunc(数字 | 列 保留小数的位数):舍弃指定位置的内 ...

  10. [Spark SQL_3] Spark SQL 高级操作

    0. 说明 DataSet 介绍 && Spark SQL 访问 JSON 文件 && Spark SQL 访问 Parquet 文件 && Spark ...