Java沙箱逃逸走过的二十个春秋(六)
字数 1902 2025-08-27 12:33:37

Java沙箱逃逸漏洞分析与防御指南

1. 受信任的方法链攻击

1.1 背景知识

受信任的方法链攻击(Trusted Method Chaining)利用了Java安全机制中的一个关键特性:安全检查时会检查整个调用堆栈。攻击的核心思想是构造一个调用链,使得调用堆栈上只包含受信任的类。

关键原理:

  • Java安全检查会遍历调用堆栈中的每一帧
  • 每帧包含方法名、类和方法签名信息
  • 攻击者通过受信任类中的反射特性调用目标方法
  • 目标方法在特权上下文中运行(如禁用安全管理器)
  • 调用堆栈上不出现不受信任的应用程序类

1.2 CVE-2010-0840漏洞分析

漏洞利用过程:

  1. 攻击者构造一个特殊的Link类,继承自java.beans.Expression(而Expression又继承自Statement)
  2. Link类伪造实现了java.util.Map.Entry接口的getValue()方法
  3. 攻击者创建包含恶意对象的JList GUI元素
  4. GUI线程绘制新元素时触发调用链

关键代码片段:

// 目标方法设置为System.setSecurityManager(null)
Object target = System.class;
String methodName = "setSecurityManager";
Object[] args = new Object[] { null };
Link l = new Link(target, methodName, args);

final HashSet s = new HashSet();
s.add(l);
Map h = new HashMap() {
    public Set entrySet() { return s; };
};
sList = new JList(new Object[] { h });

调用链解析:

  1. GUI线程调用JList的绘制方法
  2. 触发AbstractMap.toString()方法
  3. 调用伪造的Entry.getValue()方法
  4. 最终通过反射执行System.setSecurityManager(null)

调用堆栈特点:

  • 堆栈中全是JDK系统类(受信任代码)
  • 没有攻击者自己的类出现在调用堆栈中
  • 安全检查时所有调用帧都能通过验证

1.3 防御措施

Oracle通过以下方式修复了该漏洞:

  1. 修改Statement.invoke()方法
  2. 在创建Statement的代码的AccessControlContext中执行反射调用
  3. 确保不受信任的代码创建Statement时不具备必要权限

2. 序列化漏洞

2.1 背景知识

Java序列化机制允许将对象转换为字节流,反序列化则是将字节流恢复为对象的过程。当反序列化过程的部分操作在特权上下文中执行时,可能被利用来绕过安全限制。

关键风险点:

  • 特权上下文中反序列化不受信任的数据
  • 序列化协议的特殊行为(不调用某些构造函数)
  • 可以构造正常情况下无法实例化的对象(如ClassLoader)

2.2 CVE-2010-0094漏洞分析

漏洞位置:

RMIConnectionImpl.createMBean()方法的第四个参数MarshalledObject在特权上下文中被反序列化。

攻击步骤:

  1. 攻击者构造特殊的ClassLoader子类:
public class S extends ClassLoader implements Serializable {
    public static S myCL = null;
    private void readObject(java.io.ObjectInputStream in) throws Throwable {
        S.myCL = this; // 在反序列化时保存实例引用
    }
}
  1. 将该类的序列化数据作为参数传递给createMBean()

  2. 反序列化过程中:

  • 调用ClassLoader的构造函数(因为它是第一个未实现Serializable的父类)
  • 不调用S的构造函数
  • 特权上下文中的权限检查通过(因为堆栈上没有不受信任的代码)
  1. 通过readObject()方法获取ClassLoader实例

  2. 利用该ClassLoader定义具有全部权限的新类,禁用安全管理器

调用堆栈分析(漏洞版本):

1: ClassLoader.<init>()
...
15: RMIConnectionImpl$6.run()
16: AccessController.doPrivileged()  // 特权上下文
...
21: Exploit.exploit()  // 攻击代码

修复后调用堆栈:

1: ClassLoader.<init>()
...
15: RMIConnectionImpl.unwrap()
...
21: Exploit.exploit()  // 现在安全检查会失败

2.3 防御措施

Oracle通过以下方式修复了该漏洞:

  1. 移除createMBean()方法中的doPrivileged()调用
  2. 确保反序列化时进行完整的调用堆栈检查
  3. 不受信任的代码无法获取CREATE_CLASSLOADER权限

3. 综合防御建议

3.1 针对方法链攻击的防御

  1. 严格限制反射API的使用
  2. StatementExpression类的使用进行权限控制
  3. 确保安全检查时考虑完整的调用上下文

3.2 针对序列化漏洞的防御

  1. 避免在特权上下文中反序列化不受信任的数据
  2. 对序列化操作实施严格的输入验证
  3. 使用白名单机制控制可反序列化的类
  4. 考虑使用替代的序列化机制(如JSON、Protocol Buffers)

3.3 通用安全实践

  1. 及时应用Java安全更新
  2. 最小权限原则:只授予必要的权限
  3. 对安全敏感操作实施深度防御
  4. 定期进行安全审计和代码审查

4. 总结

Java沙箱逃逸漏洞展示了复杂安全系统中的典型攻击模式。通过分析CVE-2010-0840和CVE-2010-0094两个典型案例,我们可以得出以下结论:

  1. 安全机制的设计缺陷可能被组合利用
  2. 语言特性和系统API的交互可能产生非预期行为
  3. 权限检查的上下文和调用链分析至关重要
  4. 保持对Java安全更新的关注至关重要

理解这些漏洞的本质和利用技术,有助于开发更安全的Java应用程序和更有效的防御措施。

Java沙箱逃逸漏洞分析与防御指南 1. 受信任的方法链攻击 1.1 背景知识 受信任的方法链攻击(Trusted Method Chaining)利用了Java安全机制中的一个关键特性:安全检查时会检查整个调用堆栈。攻击的核心思想是构造一个调用链,使得调用堆栈上只包含受信任的类。 关键原理: Java安全检查会遍历调用堆栈中的每一帧 每帧包含方法名、类和方法签名信息 攻击者通过受信任类中的反射特性调用目标方法 目标方法在特权上下文中运行(如禁用安全管理器) 调用堆栈上不出现不受信任的应用程序类 1.2 CVE-2010-0840漏洞分析 漏洞利用过程: 攻击者构造一个特殊的 Link 类,继承自 java.beans.Expression (而 Expression 又继承自 Statement ) Link 类伪造实现了 java.util.Map.Entry 接口的 getValue() 方法 攻击者创建包含恶意对象的 JList GUI元素 GUI线程绘制新元素时触发调用链 关键代码片段: 调用链解析: GUI线程调用 JList 的绘制方法 触发 AbstractMap.toString() 方法 调用伪造的 Entry.getValue() 方法 最终通过反射执行 System.setSecurityManager(null) 调用堆栈特点: 堆栈中全是JDK系统类(受信任代码) 没有攻击者自己的类出现在调用堆栈中 安全检查时所有调用帧都能通过验证 1.3 防御措施 Oracle通过以下方式修复了该漏洞: 修改 Statement.invoke() 方法 在创建 Statement 的代码的 AccessControlContext 中执行反射调用 确保不受信任的代码创建 Statement 时不具备必要权限 2. 序列化漏洞 2.1 背景知识 Java序列化机制允许将对象转换为字节流,反序列化则是将字节流恢复为对象的过程。当反序列化过程的部分操作在特权上下文中执行时,可能被利用来绕过安全限制。 关键风险点: 特权上下文中反序列化不受信任的数据 序列化协议的特殊行为(不调用某些构造函数) 可以构造正常情况下无法实例化的对象(如 ClassLoader ) 2.2 CVE-2010-0094漏洞分析 漏洞位置: RMIConnectionImpl.createMBean() 方法的第四个参数 MarshalledObject 在特权上下文中被反序列化。 攻击步骤: 攻击者构造特殊的 ClassLoader 子类: 将该类的序列化数据作为参数传递给 createMBean() 反序列化过程中: 调用 ClassLoader 的构造函数(因为它是第一个未实现 Serializable 的父类) 不调用 S 的构造函数 特权上下文中的权限检查通过(因为堆栈上没有不受信任的代码) 通过 readObject() 方法获取 ClassLoader 实例 利用该 ClassLoader 定义具有全部权限的新类,禁用安全管理器 调用堆栈分析(漏洞版本): 修复后调用堆栈: 2.3 防御措施 Oracle通过以下方式修复了该漏洞: 移除 createMBean() 方法中的 doPrivileged() 调用 确保反序列化时进行完整的调用堆栈检查 不受信任的代码无法获取 CREATE_CLASSLOADER 权限 3. 综合防御建议 3.1 针对方法链攻击的防御 严格限制反射API的使用 对 Statement 和 Expression 类的使用进行权限控制 确保安全检查时考虑完整的调用上下文 3.2 针对序列化漏洞的防御 避免在特权上下文中反序列化不受信任的数据 对序列化操作实施严格的输入验证 使用白名单机制控制可反序列化的类 考虑使用替代的序列化机制(如JSON、Protocol Buffers) 3.3 通用安全实践 及时应用Java安全更新 最小权限原则:只授予必要的权限 对安全敏感操作实施深度防御 定期进行安全审计和代码审查 4. 总结 Java沙箱逃逸漏洞展示了复杂安全系统中的典型攻击模式。通过分析CVE-2010-0840和CVE-2010-0094两个典型案例,我们可以得出以下结论: 安全机制的设计缺陷可能被组合利用 语言特性和系统API的交互可能产生非预期行为 权限检查的上下文和调用链分析至关重要 保持对Java安全更新的关注至关重要 理解这些漏洞的本质和利用技术,有助于开发更安全的Java应用程序和更有效的防御措施。