Fastjson反序列化漏洞

前言

Fastjson 是阿里开发的一个 Java 库,用于将 Java 对象序列化为 JSON 格式,也可将字符串反序列化为 Java 对象。

Fastjson 是非常多见的 Java 反序列化漏洞,CTF中也出现的非常频繁。

Fastjson 1.2.24

Fastjson 1.2.24 版本的反序列化漏洞在 2017年 首次爆出,之后更是难以平息。

Fastjson 序列化对象的方法主要是toJSONString 方法,而反序列化还原对象的方法有3个

parseObject(String text)
parse(String text)
parseObject(Srting text, Class\clazz)

在序列化和反序列化的过程中会自动去调用反序列化对象中的 getter、setter方法以及构造函数,这就是 Fastjson 反序列化漏洞产生的原因,接下来我们来具体来看一下。

探究前的准备

首先我们先在pom.xml导入fastjson 1.2.24

image-20220311201052216

接下来我们定义如下的类来进行测试:

image-20220329192807025

属性 getter setter
public String t1
private int t2
private boolean t3
private Map t4
private Map t5

探究序列化的过程

JSON.toJSONString(String)

image-20220329193004607

在序列化字符串的时候,会调用类中所有的getter方法,我们发现最后产生的序列化字符串和平时见的有些不一样。

其实我们只需要在该方法中加入参数SerializerFeature.WriteClassName方法,传入SerializerFeature.WriteClassName可以使得Fastjson支持自省,开启自省后序列化成JSON的数据就会多一个@type,这个是代表对象类型的JSON文本。FastJson的漏洞就是他的这一个功能去产生的,在对该JSON数据进行反序列化的时候,会去调用指定类中对于的get/set/is方法。

探究反序列化过程

JSON.parse(jsonstr)

按照之前序列化的情况类比推理一下,猜测被调用的方法应该是setT1、setT2和setT5(因为T3和T4没有setter方法),然后我们执行一下该方法,看看是不是和想象中的一样

image-20220329194547129

很明显,我们的猜测也不是全无道理,只不过为什么在反序列化的过程中调用了getT4呢!我们先保留这个问题继续往下看!

JSON.parseObject(jsonstr, FastjsonTest.class)

这次我们直接执行该方法,发现它和上一个方法的结果一样!

image-20220329194734629

通过上网查阅资料,需要满足下面的条件时,getter方法就会被调用

  1. 这个getter方法不存在setter方法
  2. 返回值类型继承自Collection || Map || AtomicBoolean || AtomicInteger ||AtomicLong

上边这俩个原因是主要原因,这也就是为什么只调用了getT4而没有调用getT3,其他调用条件应该算是gettersetter方法的必须条件

方法名需要长于4
非静态方法
以 get 字符串开头,且第四个字符需要是大写字母
方法不能有参数
方法为 public 属性

JSON.parseObject(jsonstr)

我们再来看看最后一个方法,竟然执行了这么多

image-20220329194853076

仔细看一看,其实不难发现,调用的前四个方法和之前那俩个一样,然后接着调用了所有的getter方法,所以也就不难理解了

反序列化后的类型

我们再查看三种反序列化方法的返回类型

image-20220329195206035

我们通过调试查看了这三种方法返回的类型,发现JSON.parseObject(jsonstr)方法还真是独特,分析得知:

parseObject(String text)其实就是执行了parse(),随后将返回的Java对象通过JSON.toJSON()转为JSONObject对象

总结

JSON.parse(jsonstr) JSON.parseObject(jsonstr, FastjsonTest.class) JSON.parseObject(jsonstr)
T1 setter setter setter getter
T2 setter setter setter getter
T3 getter
T4 getter getter getter getter
T5 setter setter setter getter

上述为测试结果,下边为属性的类型和有无getter/setter

属性 getter setter
public String t1
private int t2
private boolean t3
private Map t4
private Map t5
  1. 反序列化过程中会调用所有的setter方法
  2. 如果没有setter方法,会调用满足条件的getter方法
方法名需要长于4
非静态方法
以 get 字符串开头,且第四个字符需要是大写字母
方法不能有参数
返回值类型继承自Collection \|\| Map \|\| AtomicBoolean \|\| AtomicInteger \|\|AtomicLong
getter 方法对应的属性只能有 getter 不能有setter方法
方法为 public 属性

happyFastjson

可以很容易的找到fastjson反序列化的入口

image-20220312171609724

发现FlagBean类中可以读取到flag

image-20220312171800310

于是构造payload

poc={
    "a":{
        "@type":"com.ctfshow.happyfjs.Beans.FlagBean",
        "flag":{
            "@type":"java.util.HashMap"
    	}
    }
}

该payload现实经过JSON.parse(poc)调用了所有setter方法

  1. 这里没有setFlag方法,并且满足其他执行getFlag的条件,所以执行了getFlag方法
  2. 序列化字符串中没有free属性,所以不执行setFree方法

此时因为this.count=1所以并没有执行系统命令,然后这里又对返回的对象进行了toString方法,通过调试发现,实际上这里还是调用了toJSONString方法

image-20220330141042688

所以相当于再次进行了序列化操作,所以会执行所有的getter方法,然后就会输出flag

这里我尝试将poc修改为如下:

poc={
    "@type":"com.ctfshow.happyfjs.Beans.FlagBean",
    "flag":{
        "@type":"java.util.HashMap"
    }
}

通过调试发现调用的是一下方法

image-20220330142006231

这个方法是类对象的默认toString方法,我们可以通过覆写来实现新的toString方法

posted @ 2023-01-08 23:45  seizer-zyx  阅读(1952)  评论(0编辑  收藏  举报