【创宇小课堂】代码审计-Fastjson各版本漏洞分析(上)

admin 2022年4月15日00:44:05评论52 views字数 4564阅读15分12秒阅读模式

- 前言 -

最先出现问题的Fastjson 1.2.24反序列化漏洞已经分析过了,产生漏洞的原理也差不多理解了

在1.2.25之后的版本,以及所有的.sec01后缀版本中,autotype功能默认是受限的(黑白名单机制)

在1.2.68之后的版本,fastjson增加了safeMode的支持。配置safeMode后,无论白名单和黑名单,都不支持autoType

- 概念 -


可能出现一些新的概念,给一些参考链接吧

FastJSON为什么要有autoType功能

enable_autotype

fastjson_safemode

- 演示代码 -


后面的分析代码都以此为基础修改

package org.example;

importcom.alibaba.fastjson.JSON;

publicclass App {
    publicstaticvoidmain(String[] args){
        String json ="{"@type":"org.example.User","age":66,"username":"test"}";
        System.out.println(JSON.parseObject(json));
    }

}

class User {
    privateString username;
    privateint age;

    publicvoidsetUsername(String username){
        this.username= username;
        System.out.println("call setUsername");
    }

    publicString getUsername(){
        System.out.println("call getUsername");
        return username;
    }

    publicvoidsetAge(int age){
        this.age= age;
        System.out.println("call setAge");
    }

    publicintgetAge(){
        return age;
    }
}


- fastjson 1.2.24 - 

之前已经分析过了,就不在写了


fastjson 1.2.41

利用的前提是必须要手动开启autoTypeSupport,不然还是不能利用,所以说还是有一点鸡肋吧

从代码中开启autoTypeSupport

ParserConfig.getGlobalInstance().setAutoTypeSupport(true);

在1.2.25之后的版本,以及所有的.sec01后缀版本中,增加了checkAutotype函数,autotype功能默认是受限的(黑白名单机制)

但在1.2.25到1.2.41之间,发生过一次checkAutotype的绕过。

Payload如下

{"@type":"Lorg.example.User;","age":66,"username":"test"}

我们用这个payload来分析一下如何绕过的(fastjson 1.2.41)

进入checkAutoType后,首先会对typeName的长度进行判断,很明显这个条件满足不了,所以不会抛出异常

【创宇小课堂】代码审计-Fastjson各版本漏洞分析(上)


继续向下,开启autoTypeSupport时,会先通过黑白名单来判断,先白名单后黑名单

【创宇小课堂】代码审计-Fastjson各版本漏洞分析(上)


很明显我们传入的typeName Lorg.example.User;肯定是不在黑名单内的,这是一个绕过的点

【创宇小课堂】代码审计-Fastjson各版本漏洞分析(上)


继续向下,如果clazz==null,就会调用TypeUtils.getClassFromMapping(typeName);,跟一下其实就是从一个ConcurrentHashMap中看看存不存在这个类,很明显我们传入的L开头的类是不会存在的

【创宇小课堂】代码审计-Fastjson各版本漏洞分析(上)


继续向下,和上面类似,我们这个类还是找不到的,所以clazz还是null

【创宇小课堂】代码审计-Fastjson各版本漏洞分析(上)


没开启autoTypeSupport的情况下,依然会进行黑白名单检测,先黑名单后白名单,我们这里手动开启了所以这里不管,因为会跳过

【创宇小课堂】代码审计-Fastjson各版本漏洞分析(上)


前面黑名单检测都没问题,就会开始加载这个类了

【创宇小课堂】代码审计-Fastjson各版本漏洞分析(上)


跟进loadClass,如果第一个字符是[,就会去掉[再去解析,我们这里不满足就先不看,继续向下

【创宇小课堂】代码审计-Fastjson各版本漏洞分析(上)


这个条件就是这次绕过的核心条件了

elseif(className.startsWith("L")&& className.endsWith(";")){
    String newClassName = className.substring(1, className.length()-1);
    returnloadClass(newClassName, classLoader);
}

如果开头是L而且结尾是;,那么就会给前后这俩字符去掉,所以可以看到我们的newClassName就是我们想要的org.example.User

【创宇小课堂】代码审计-Fastjson各版本漏洞分析(上)


后续就会加载我们的类实例化,达到我们绕过的目的

【创宇小课堂】代码审计-Fastjson各版本漏洞分析(上)

debug过程中,可能大家注意到一个点,loadClass函数中,有一个条件,如果第一个字符是[,就会去掉[再去实例化,那这个地方是不是也能用来绕过呢?

答案是当然可以,这个绕过点就体现在1.2.43版本中

- fastjson 1.2.42 -


1.2.41问题出现后,1.2.42中尝试了修复,修复方式

https://github.com/alibaba/fastjson/commit/e701faa2da7cff6d94394061bbff06a166c2aaaf

寻找历史commit技巧:

1.release里面找对应的版本的commit

2.直接搜索commit

3.直接搜索issue

可以明显的看到,给原来的denyList变成了denyHashCodes,让安全研究更难了,但是hashcode的方法是公开的,只要jar包够多还是可以碰撞出来的,感觉治标不治本。。。

【创宇小课堂】代码审计-Fastjson各版本漏洞分析(上)


同时可以看到针对漏洞绕过的修复方式,很简单粗暴,如果发现开头是L而且结尾是;,就直接去掉

【创宇小课堂】代码审计-Fastjson各版本漏洞分析(上)


所以绕过方式也很简单,直接用2个L和2个;就可以了,Payload如下

{"@type":"LLorg.example.User;;","age":66,"username":"test"}

- fastjson 1.2.43 -


LL;;可以绕过的情况做了过滤,如果只有一个L;,就去除了后再走黑名单去过滤看看是否允许反序列化,着实太恶心了看着

【创宇小课堂】代码审计-Fastjson各版本漏洞分析(上)


所以2个LL;;是行不通了,但是别忘了我们在分析1.2.41的时候,发现还会去掉[然后实例化,这就是绕过点

初始payload

{"@type":"[org.example.User","age":66,"username":"test"}

报错exepct '[', but ,, pos 29, json : {"@type":"[org.example.User","age":66,"username":"test"},29那个位置,期望一个[,但是是,,所以我们加一个[

{"@type":"[org.example.User"[,"age":66,"username":"test"}

报错syntax error, expect {, actual string, pos 30, fastjson-version 1.2.43,期望30的位置是一个{,加上

最终POC

{"@type":"[org.example.User"[{,"age":66,"username":"test"}

【创宇小课堂】代码审计-Fastjson各版本漏洞分析(上)

看着有点迷,为啥加上[{就可以了?

分析一下,通过checkAutoType后,返回class [Lorg.example.User;

【创宇小课堂】代码审计-Fastjson各版本漏洞分析(上)


一直跟,发现调用了deserializer.deserialze,跟进去,发现使用了clazz.getComponentType(),是不是很眼熟?就是前面去掉[的那个地方

【创宇小课堂】代码审计-Fastjson各版本漏洞分析(上)

这个函数是native的,所以看不到代码。。。不过根据结果来看,就是去掉[L;拿到类

【创宇小课堂】代码审计-Fastjson各版本漏洞分析(上)


再继续往下,跟进parseArray

【创宇小课堂】代码审计-Fastjson各版本漏洞分析(上)


发现如果token != 14就会抛出错误,而没有[的时候,token是16,所以会报错,{也类似,可以下个异常断点来分析

【创宇小课堂】代码审计-Fastjson各版本漏洞分析(上)

最后看下到setXXX的运行堆栈信息,结合堆栈来分析可以节约很多时间

setUsername:20, User (org.example)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
setValue:110, FieldDeserializer (com.alibaba.fastjson.parser.deserializer)
parseField:118, DefaultFieldDeserializer (com.alibaba.fastjson.parser.deserializer)
parseField:1061, JavaBeanDeserializer (com.alibaba.fastjson.parser.deserializer)
deserialze:756, JavaBeanDeserializer (com.alibaba.fastjson.parser.deserializer)
deserialze:271, JavaBeanDeserializer (com.alibaba.fastjson.parser.deserializer)
deserialze:267, JavaBeanDeserializer (com.alibaba.fastjson.parser.deserializer)
parseArray:729, DefaultJSONParser (com.alibaba.fastjson.parser)
deserialze:183, ObjectArrayCodec (com.alibaba.fastjson.serializer)
parseObject:373, DefaultJSONParser (com.alibaba.fastjson.parser)
parse:1338, DefaultJSONParser (com.alibaba.fastjson.parser)
parse:1304, DefaultJSONParser (com.alibaba.fastjson.parser)
parse:152, JSON (com.alibaba.fastjson)
parse:162, JSON (com.alibaba.fastjson)
parse:131, JSON (com.alibaba.fastjson)
parseObject:223, JSON (com.alibaba.fastjson)
main:10, App (org.example)

【创宇小课堂】代码审计-Fastjson各版本漏洞分析(上)

原文始发于微信公众号(安全宇宙):【创宇小课堂】代码审计-Fastjson各版本漏洞分析(上)

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年4月15日00:44:05
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   【创宇小课堂】代码审计-Fastjson各版本漏洞分析(上)https://cn-sec.com/archives/911317.html

发表评论

匿名网友 填写信息