Java沙箱逃逸走过的二十个春秋(六)
字数 1902 2025-08-27 12:33:37
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()方法- 攻击者创建包含恶意对象的
JListGUI元素 - 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 });
调用链解析:
- 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子类:
public class S extends ClassLoader implements Serializable {
public static S myCL = null;
private void readObject(java.io.ObjectInputStream in) throws Throwable {
S.myCL = this; // 在反序列化时保存实例引用
}
}
-
将该类的序列化数据作为参数传递给
createMBean() -
反序列化过程中:
- 调用
ClassLoader的构造函数(因为它是第一个未实现Serializable的父类) - 不调用
S的构造函数 - 特权上下文中的权限检查通过(因为堆栈上没有不受信任的代码)
-
通过
readObject()方法获取ClassLoader实例 -
利用该
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通过以下方式修复了该漏洞:
- 移除
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应用程序和更有效的防御措施。