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逃逸攻击的防御策略。
目录
3.2.3 CVE-2013-2423
3.2.4 CVE-2013-1493
3.3 本章小结
4 JRE 漏洞挖掘研究
4.1 针对 Java API 设计缺陷的漏洞挖掘工作
4.2 针对Java原生层漏洞的漏洞挖掘工作
4.2.1 符号执行技术简介
3.2.3 CVE-2013-2423
作为一门面向对象的语言, Java中所有的类型也都有对象的定义类。例如int类型的定义类是java.lang.Integer, long类型为java.lang.Long, float类型则为java.lang.Float, double类型为java.lang.Double。通过查看它们的源码,可以看到上述每个class都有一个名为"TYPE"的域,以Integer类为例,其"TYPE”域定义如表3-15 所示。
在 Java 程序执行时,JVM 根据这个 TYPE 域来判断当前变量是何种类型,需要分配多少空间。那么,通过一定的手段修改掉这个TYPE域,理论上就可以达到 Type Confusion 的目的。
第二章中提到Java 7新增了功能更加强大、调用更加方便的反射API,其中findStaticSetter可用于设置域的值。CVE-2013-2423231便是这样利用这个反射API,对TYPE域进行了恶意篡改,从而在Java API层面实现了Type Confusion.下面给出这个漏洞利用原理的分析。
首先要明确,在32位的JVM中, int值占4字节, double与long是占8字节,另外值得注意的是, Java语言中有一个基类Object,所有的类都继承自它,在hotspot源码中可以看的它的声明,在这里只需知道一个Object对象也是占用4字节(可以理解为指针,其实底层也是这样实现的)。第一步,构造三个辅助类,如表3-16 所示。
第二步,通过反射API获得修改Long和Integer类中TYPE域的方法句柄,如表3-17 所示。
第三步,通过反射API获取辅助类Union1与Union2的int域field1,如表3-18所示。
第四步,通过 invoke 的方式执行 mhl 与 mh2,也就是执行 Type Confusion这里需要注意的是,在 invoke 之前,先把 Long 与 Integer 原本的 TYPE 域记录下来,以便混淆利用之后将其恢复正常,如表3-19所示。这段代码将Long的TYPE 域设置为 int.class,同时将 Integer 的 TYPE 域设置为其他非 int 类型。混淆过后,当JVM遇到 int型变量时,会上溯到TYPE等于int.class的那个类,此时即为Long。值得注意的是,int是4字节而long是8字节,在执行后续操作时,JVM 会认为 int 类型是8 字节。
混淆工作完成后,使用反射AP1可以将Union2的SystemClass设置为真正的java.lang.System类。具体实现代码如表3-20所示。
当Field的get/set方法执行时,由于field1的声明为int型,此时JVM会认为它是8 字节的 long 型,而实际上,ul 与 u2 的 field1 都没有实际赋值,在 set方法执行时,8 字节的数据(包括4 字节的field1 和4 字节的Object指针--也就是 System.class 对象)会被拷贝到 u2 中, u2 的 field2 也就被设置为自定义的SystemClass类。原本不能被任意修改的System类,被混淆为可以任意修改的SystemClass,从而形成一个可以利用的Type Confusion.
第五步,恢复Integer与Long的TYPE域,如表3-21所示。
第六步,将securityManager对象设置为null。辅助类SystemClass是由30个Object 组成,而且已经成功混淆为真正的 System 类对象。这样通过搜索这30 个Object,总可以找到 securityManager 对象,经过不断实验发现,在Windows系统下,该对象总是稳定匹配第29 或第30 个 Object.PoC 代码如表3-22 所示。
3.2.4 CVE-2013-1493
由于 Java 是类型安全的语言,Java 具有健壮的内存异常捕获机制,假如要创建一个数组,其size最大值是由JVM限制的。例如开发者编写Java代码创建一个大小为0x7FFFFFFF的数组,编译执行,会发现Java控制台打印了异常信息"java.lang.OutOfMemoryError:Requested array size exceeds VM limit",说明JVM拒绝了这种不安全的大量内存申请。但利用CVE-2013-1493这样的漏洞,可以绕过JVM的类型安全机制,下文将给出该漏洞分析。
3.3 本章小结
本章主要介绍了 JRE 漏洞的类型,针对 Java API 设计缺陷、类型混淆、Java原生层漏洞,以及 Java Applet 自签名问题分别给出阐述,分别列举出4个漏洞:CVE-2012-4681, CVE-2012-5076, CVE-2013-2423, CVE-2013-1493,并给出漏洞原理分析。
4 JRE 漏洞挖掘研究
在软件安全领域,漏洞挖掘一直是最为热门的研究方向之一。在日趋激烈的攻防技术研究或攻防实战中,无论攻击者还是防御者,掌握漏洞挖掘技术意味着掌握了攻防的主动权。对于软件厂商和广大用户而言, Oday漏洞(特指被攻击者掌握却尚未被软件厂商修复的漏洞)是最大的潜在威胁。为了保证软件的质量,软件发布前的测试环节备受关注。如何提高测试效率,或者说提高挖掘潜在漏洞的能力,尽量避免在软件发布后出现0day漏洞,已经成为一个热门的研究方向。
4.1 针对 Java API 设计缺陷的漏洞挖掘工作
通过第三章中对 JRE 漏洞的分析,可以总结出缺陷 Java API 具备的一些特点:
1. 存在一条信任调用链(Trusted Invoke Chain)
2. API 参数可控;
3. 关键代码被 doPrivileged block 包裹;
4. 被调用后可以完成获得任意类对象、获得任意方法对象、获得任意域对象、以BootstrapClassloader来加载任意指定类等等。
所谓“信任调用链”是指,该API或者可以被直接调用,或者其本身不能被直接调用,但却间接地被其他可直接调用的 API 所调用。
由于API设计缺陷具备一些共同特征,而且源代码可以方便地获得,针对该问题的漏洞挖掘更适合于进行源代码审计。源代码审计的步骤为:
1. 分析已有 Java API 设计缺陷范例,建立漏洞模型和审计规则;
2. 确定审计对象,对于可获得源代码的版本,以源代码为审计对象,对于不可获取源代码的版本,通过逆向工程(class 文件的反编译)得到可读性较高的源代码;
3. 通过文本关键字搜索,在审计对象中提取包含doPrivileged代码块的类:
4. 对第3 步中的所有类按照制定好的规则进行人工源代码审计,匹配已建立的漏洞模型;
5. 找到疑似漏洞,尝试构造 PoC以进行漏洞验证。
4.2 针对Java原生层漏洞的漏洞挖掘工作
第二章中提到,作为一种类型安全的编程语言,JRE 提供了一系列的原生层层安全机制,如结构化的内存访问、自动化垃圾收集、数组越界检查、空指针引用检查、严格的类型转换检查等。基于这些安全机制考虑,Java被认为是一种较为安全的语言。然而近年来,JRE漏洞不断被发现,其漏洞类型包括利用Java API逻辑缺陷的沙箱绕过,还有原生层代码(Native Code)级别的漏洞,也称 JRE 原生层漏洞。
4.2.1 符号执行技术简介
在众多灰盒测试技术中,符号执行(Symbolic Execution)是一种代码执行空间遍历技术,早在1975年就由Boyer等人提出,并将其用于程序测试和调试领域。但是由于当时硬件条件不能满足符号执行所需的计算能力,该技术并没有得到广泛采用。随着硬件技术的快速发展,符号执行又被重新提出,并广泛应用于程序分析、漏洞挖掘等领域。
符号执行是用抽象符号代替程序变量,根据程序的语义,在每条路径上通过符号执行引擎对抽象符号作收集、合并、归约等计算,最终得出每条路径的路径条件。从数学的角度来看,就是一个包含符号和运算符的逻辑表达式。