Java语言是最为流行的面向对象编程语言之一, Java运行时环境(JRE)拥有着非常大的用户群,其安全问题十分重要。近年来,由JRE漏洞引发的JVM逃逸攻击事件不断增多,对个人计算机安全造成了极大的威胁。研究JRE安全机制、JRE漏洞及其挖掘、JVM逃逸攻防技术逐渐成为软件安全领域的热门研究方向。
针对Java层API与原生层API, JRE安全机制分别包括JRE沙箱与JVM 类型安全机制。本文针对JRE沙箱组件及其工作原理进行剖析,总结其脆弱点;分析调研JVM安全机制,提出其脆弱点在于Java原生层漏洞,为JRE漏洞挖掘工作提供理论基础。
对于JRE漏洞,本文进行漏洞分类研究,提取Java API设计缺陷、Java原生层漏洞两种JRE漏洞类型的典型漏洞进行分析,总结漏洞特征,为漏洞挖掘工作建立漏洞模型。
根据JRE漏洞分析中建立的漏洞模型,本文采用源代码审计的方法开展Java API设计缺陷类型的漏洞挖掘工作,发现了数个Oracle JRE、OpenJDK和Apple JRE 的 Java API 设计缺陷问题。在 Java原生层漏洞挖掘工作中,出于Java原生层漏洞的特殊性,本文基于程序分析领域的符号执行技术提出一种寄存器符号化监控方法,选取开源符号执行平台S2E作为漏洞挖掘工具,并且基于其实现了针对JRE原生层漏洞挖掘的辅助插件 SymJava 和 SymRegMonitor,基于 OpenJDK 和 Oracle JRE逆向代码进行源代码白盒审计并构建了用于进行漏洞挖掘的 Java 测试用例,最后对36个调用Java原生层API的Java测试用例进行实际测试发现了共计6 个 JRE原生层安全隐患,其中2 个可被攻击者恶意利用,并给出漏洞分析和 PoC。
针对 JVM 逃逸攻防问题,本文分别从攻击和防御角度,提出 JVM逃逸攻击的5 个关键元素,针对每个元素进行攻防技术研究,并通过绕过杀毒软件静态检测的实验证明了本文提出的 JVM 逃逸攻击技术。最后,本文从多角度给出JVM逃逸攻击的防御策略。
目录
4.2.2 寄存器符号化监测方法
4.2.3 SymJava 与 SymRegMonitor 设计与实现
4.2.4 源代码审计与 Java 测试用例构建
4.2.5 漏洞挖掘测试实验
4.3 漏洞挖掘研究成果展示与分析
4.3.1 Sun Toolkit 导致系统属性泄漏
4.3.2 SecuritySupport 导致系统属性泄漏
4.3.3 Mac OS X 本地文件探针漏洞
4.3.4 任意类对象获取漏洞
4.3.5 利用 JRE 原生层漏洞实现远程代码执行
4.3.6 其他JRE原生层安全问题
4.4 本章小结
4.2.2 寄存器符号化监测方法
首先给出寄存器符号化的定义:
假设有一段代码,函数foo有一个int类型的参数a,通过S2E的符号标记API,可以将其标记为符号值,记为x,然后编译该代码,拷贝进入Qemu加载的镜像中执行。在执行过程当中,遇到与x相关的任何运算时都记作一个算术表达式。执行到某处时,从Klee的指令解析模块中发现寄存器R的值等于bx+c(b和c为常量或其他符号值或其他算术表达式),就称符号x的扩展覆盖寄存器R,或称寄存器 R 符号化。
寄存器符号化证明存在一条 source-and-sink32路线:攻击者通过精心构造参数a覆盖ESP,可以让程序跳转到自己想要的位置,比如一段具备攻击性质的shellcode。针对这样的寄存器覆盖问题,本文提出寄存器符号化监控方法。
4.2.3 SymJava 与 SymRegMonitor 设计与实现
S2E 原生的符号标记 API 只能针对 Linux 下的 C代码,因此我们开发了插件SymJava,以JNI的方式标记Windows平台下Java代码中的变量。SymJava的工作原理如图4-2 所示。
SymJava可以标记的变量类型和API如表4-3所示。
通过调用表4-3 中的 API,可以得到相应类型的符号值。例如,要将一个 Java代码中的int型变量标记为符号值,伪代码如表4-4所示。
S2E 除了三大模块之外,还提供给开发者良好的扩展性,开发者基于 S2E 开发了一些用于程序分析的辅助插件【33】,例如用于监控Windows平台指定进程运行状态的WindowsFunctionMonitor,用于生成样本数据的TestcaseGenerator,用于驱动程序漏洞挖掘的SymDrivel341等等。为了实现寄存器符号化的监控,我们开发了SymRegMonitor。其架构如图4-3所示。
以寄存器EAX的监控为例, SymRegMonitor的伪代码如表4-5所示。
4.2.4 源代码审计与 Java 测试用例构建
使用符号执行平台进行漏洞挖掘工作之前,需要筛选出可以构建测试用例并符合S2E符号标记规则的Java Native API。本研究采取源代码人工审计的方法来进行该筛选工作,并选取 OpenJDK 7 作为源代码审计对象。
相对的,进行漏洞挖掘的目标API可分为以下两类:
第Ⅰ类,可以被直接调用的公有 Native API;
第II类,存在 API 调用链的可被外界干扰的私有 Native API。
在第 Ⅰ 类当中,static 关键字修饰的静态 Native API 调用起来更加简单,一旦存在漏洞,其利用就变得非常简单。所以在本项研究中,特别提出第1类(第I 类的子集,即I'CI:
第I'(I'cI)类,static 关键字修饰的公有 Native API.
本文的源代码审计工作基于寻找上述第Ⅰ'类与第II类目标 API而展开,由于Oracle JRE 并不开源,需要通过 jad 等 class 文件逆向工具进行反编译,工作量较大。本文进行的研究针对开源的OpenJDK(7-b147版本)源代码进行审计,确认发现第Ⅰ'类或第II类 API之后,再逐一对 Oracle JRE 的同一 class 文件进行反编译并作比对,最终确定所有进行漏洞挖掘的目标API,其类型和数量如表4-7所示。
本研究选取了:
com.sun.org.apache.xalan.internal.utils.ObjectFactory.findProvideClass
这一缺陷 API。该 API 通过传入一个字符串类型的参数(指定要获取 class 的全限定名)可以获取任意包中的class对象,继而可以通过构造Expression表达式调用任意第1'类API,详细分析见4.3.4小节。该过程伪代码如表4-8所示。
4.2.5 漏洞挖掘测试实验
本实验使用了开源符号执行平台 S2E,基于插件 SymJava 和 SymRegMonitor对36个Native API构造的测试用例进行了测试。由于符号执行是计算资源密集型的方法,本实验选取了配置有12核24线程Intel Xeon处理器和72GB内存的计算机作为测试机器,系统环境为Ubuntu 12.04 Server。
4.3 漏洞挖掘研究成果展示与分析
本小节给出4.1 小节以及4.2小节中漏洞挖掘工作的研究成果,并作分析。
4.3.1 Sun Toolkit 导致系统属性泄漏
漏洞存在于 sun.awt.SunToolkit.getSystemProperty,在白盒审计版本 OpenJDK7-b147 时发现。该方法的源代码如表4-10 所示。
这个方法具备缺陷 API 的所有特征:存在信任调用链——可以构造表达式Expression 间接调用,参数可控——可以控制系统属性字符串参数,关键代码被doPrivileged 代码块包裹——获取系统参数的方法 System.getProperty,从而造成系统属性隐私信息泄露。利用方法也很简单,通过构造一个Expression来执行该方法,与 CVE-2012-4681 类似。不过在 Oracle JRE 7 中的 SunToolkit 类删除了这个方法,修补方式也跟 CVE-2012-4681 一样。
4.3.2 SecuritySupport 导致系统属性泄漏
漏洞存在于如下类中:
com.sun.org.apache.xalan.internal.utils.SecuritySupport.getSystemProperty
在审计版本Apple JRE 6u27时发现,该方法源码如表4-11所示(由Windows下的 jad 反编译得到)。
这里再补充一点,可以获取到的系统属性是 System.getProperty 所提供的,主要包括系统用户名、主目录、操作系统类型和版本、JRE版本、JRE安装路径、JVM 版本号等等,如表4-12所示。
4.3.3 Mac OS X 本地文件探针漏洞
漏洞存在于类 com.apple.eio.FileManager.moveToTrash 中,在审计版本 Apple JRE 6u27时发现。该方法的源代码如表4-13所示。
4.3.4 任意类对象获取漏洞
漏洞存在于如下类中:
com.sun.org.apache.xalan.intemal.utils.ObjectFactory. findProviderClass.
在审计版本Apple JRE 6u27时发现,该方法源码如表4-14所示(由Windows下的 jad 反编译得到)。
该方法的逻辑为:若securityManager不为null,就直接返回Class.forName的结果。通过控制字符串参数s,可以获取到任意类的class对象。假如获取的目标类含有public static的存在漏洞的API,就可以配合这个缺陷API构造一个Expression来调用这个存在漏洞的API.
4.3.5 小节将详细介绍这一漏洞利用思路。
4.3.5 利用 JRE 原生层漏洞实现远程代码执行
该原生层漏洞所在位置:
sun.java2d.DefaultDisposerRecord.invokeNativeDispose.
该方法声明如下所示:
public static native void invokeNativeDispose(long
disposerMethodPointer, long dataPointer);
通过查看OpenJDK的Native Code源码可知,这个native方法的具体实现为:将disposerMethodPointer和dataPointer两个长整型参数转化为指针,其中disposerMethodPointer被转化为函数入口指针(相当于call这个指针指向的地址),所以disposerMethodPointer的值可以覆盖EIP; dataPointer指向的是被“call"方法所需的参数,会覆盖EAX地址,这样以来,结合堆喷射技术,可以执行shellcode
由于这个类位于sun.包,是在访问限制列表中的。但通过4.3.4小节提到的Java API设计缺陷,可以得到该类的一个class对象,再通过构造Expression,可以调用到缺陷方法invokeNativeDispose。
4.3.6 其他JRE原生层安全问题
SymRegMonitor发现的6个安全问题所在的API、寄存器符号化情况以及可造成的影响如表4-15所示。
4.4 本章小结
本章申明JRE漏洞挖掘研究的背景和意义,针对Java API设计缺陷和Java原生层漏洞分别制定不同的漏洞挖掘方案:通过白盒审计和历史漏洞规则制定相结合审计了若干版本的JRE,发现了数个Java API设计缺陷;而针对原生层漏洞的挖掘工作,则引入符号执行理论,基于开源框架S2E开发了用于检测寄存器符号化的插件SymJava和SymRegMonitor,结合白盒审计、测试用例构建,最终通过实验发现了数个Java原生层安全问题。在本章4.3 小节,对本研究中的漏洞挖掘工作发现的安全问题作了总结和分析。