freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

Fastjson反序列化漏洞原理分析
2024-09-12 16:51:59

0x01 前言

最近学习了Fastjson反序列化漏洞,看视频、读文章,才勉勉强强搞明白原理。这里我把学习到的内容简单总结出来,希望对各位理解Fastjson反序列化漏洞有所帮助。如果文章中有错误之处,还请批评指正,我会尽快修改。

0x02 前置知识

什么是json字符串?

形如下面的字符串,就被称为一个json字符串,也就是字典的形式

{

"name":"BossFrank",

"age":23

}

Fastjson是什么?

一个开源的java工具库,用来把Java对象序列化成json字符串或者把json字符串反序列化成一个Java对象

对象属性动态赋值的方式

1、调用对象的getter/setter方法

2、反射赋值

安全问题

FastJson引入了一个特性,即可以通过@type字段来指定反序列化出来的对象,该字段的值为对象所属类的全限定名。由于这个@type,在Fastjson反序列化的时候,可以指定任意反序列化的类。如果用户传入恶意的JSON字符串,那么就会产生安全问题

FastJson的序列化和反序列化函数

序列化函数:JSON.toJSONString(Object object)

反序列化函数:Fastjson反序列化采用JSON.parseObject()和JSON.parse()这两个方法


parseObject():返回JSON对象

第一处parse返回出来的对象,就是我们普通的java对象,然后会进行逻辑判断,最终转成JSON对象返回出来,所以,无论是否使用@type来指定对象,返回出来的只会是JSON对象。

1726109601_66e257a1562d194404e9c.png!small?1726109600757

1726109618_66e257b2b82cfcc26bde5.png!small?1726109618058


而调用parseObject想要获取到普通的对象,有两者方式,一种是对parseObject传第二个参数,即想要返回对象的类

1726109674_66e257ea145cdbd1069ad.png!small?1726109673552

实际上呢,这里调用的并非前面的那个parseObject,而是它的一个同名函数

1726109893_66e258c5886ad5bdc4cd2.png!small?1726109892864

而第二种方式呢,就是拿到JSON对象之后,调用toJavaObject函数

1726109923_66e258e30bafac4520794.png!small?1726109922485

1726109946_66e258fa5ca7f996646e7.png!small?1726109945730


Parse():返回我们本身的类对象

1726109981_66e2591d8cd0138584c9b.png!small?1726109980875

可以看到我们用@type指定类型之后,返回出来的是person对象

1726110036_66e259546f7da3c600015.png!small?1726110036156

同时我们注意到,在调用parse和parseObject的时候,会自动调用person类的getter和setter方法,至于为什么会调用,后面流程分析会知道。同时还发现两者调用的getter/setter方法有差异,即parseObject会调用getter和setter方法,而parse时只会调用setter方法

这里差别的原因可以先粗浅理解为:parseObject会先创建一个普通的java类对象,这里是person对象,前面我们知道,给对象属性赋值,要么反射,要么set方法,这里要给person对象的属性赋值,所以要调用set方法,又因为返回时要转成JSON对象{"name":"mike","age":17},所以要调用get方法来获取每个属性的值

0x03流程分析

在parseObject处下个断点,跟进去看,发现调用了只接受字符串的parse函数

1726110511_66e25b2fe814aa9764ea0.png!small?1726110511566

继续往里跟,然后调用到parse(String text, int features),也就是说只接受字符串的parseObject函数和parse函数最后都会调用到这里

1726110530_66e25b4271d2d31a7f951.png!small?1726110529967

这里实例化了一个默认的Json解析器,然后调用了一个不接受参数的parse函数,继续往里跟,最后会来到一个接受Object类型参数的parse函数,这个函数里面的逻辑是,获取令牌,根据令牌的类型进行解析,比如说左大括号、JSON数组、左方括号等等

1726110665_66e25bc917df508af0917.png!small?1726110664459

因为我们是JSON字符串,也就是左大括号,然后会走到这里

1726110673_66e25bd1443d010170b3e.png!small?1726110672792

new了一个JSON对象,然后传给了DefaultJSONParser里面的parseObject(final Map object, Object fieldName)函数,继续跟进去,开头有一些边界值的检查,核心逻辑就是这里try里面的内容

1726110687_66e25bdf0e865eba7d463.png!small?1726110686458

继续往下走,定义了一个key,下面就是一些if-else来获取到key的值

1726110707_66e25bf33753801ddf570.png!small?1726110706698

然后就会走到这里,对key值进行判断,我们实际上key值就是JSON.DEFAULT_TYPE_KEY,也就是下面会按照Java对象解析,所以会走到这个if里面

1726110877_66e25c9d07d1e403b1d3c.png!small?17261108766761726110818_66e25c628a947cc087211.png!small?1726110818026

if里面首先把类加载进来,然后就是一些if判断,最主要的还是最后这里

1726110900_66e25cb499ea7d9d9e64b.png!small?1726110899951

获取了person类的反序列化器,然后用这个反序列化器进行反序列化,这里返回就是我们的person对象了,我们跟进去getDeserializer这个函数

1726110928_66e25cd0a3a55ad144a04.png!small?1726110928203

这个函数里面,先根据我们的type尝试从缓存中获取一个已经存在的反序列化器,这里是没有的,所以不会直接返回,然后就调用到ParserConfig里的一个同名函数getDeserializer((Class<?>) type, type),我们跟进去

1726110947_66e25ce3291cce94df299.png!small?1726110946618

在这里有一个黑名单,继续往下走过一些大大小小的if之后,最后会来到这里,创建一个JavaBean反序列化器,跟进去

1726110964_66e25cf4cb0191fbd8479.png!small?1726110964397

先看到里面有个按钮asmEnable,默认为true

1726110981_66e25d0515df794fa3051.png!small?1726110980609

然后下面有一些if判断,可以改变asmEnable的值,接着到这里,新建一个JavaBeanInfo。这里要知道的是,在创建类的反序列化器的时候,要先了解类里面的一些内容,比如字段、构造函数、setter/getter函数等,然后把这些内容放到JavaBeanInfo里面去,我们跟进去

1726111055_66e25d4f5b204d21bd7ce.png!small

这里获取person类的所有字段和方法

1726111079_66e25d67ad9963b177e4b.png!small?1726111079214

然后继续往下走到这里,大概说一下这三处循环的作用,分别是获取这个类满足条件的所有setter方法、字段和getter方法

1726111091_66e25d73325aa2a1b8ade.png!small?1726111090716

简单说一下第一处循环的逻辑,遍历所有的方法,开头有一些if判断,目的是找到满足以下条件的方法:

  • 方法名长度大于4
  • 不是静态函数
  • 返回值为void
  • 函数接受一个参数
  • set开头

接着截取setxxx后面的字段名,转成小写,然后把获取到的信息放到new出来的FieldInfo,加到fieldList里面去

1726111103_66e25d7f68c83b7fb2a99.png!small?1726111103288

第二处循环的大概逻辑是添加了public、private、未在列表中存在等一些字段到fieldList中

第三处循环与第一处差不多,找到满足条件的方法加到fieldList,其实也就是getter方法,这里要满足的条件大概是:

  • 方法名长度大于4
  • 不是静态函数
  • get开头
  • 返回值必须为Collection、Map、AtomicBoolean、AtomicInteger、AtomicLong其中之一
  • 函数接受参数为0
  • 并且前面没有被加到过FileList的字段,也就是说没有对应的setter方法

然后也会上面处理set一样,截取getxxx后面的字段名,转成小写,加到fieldList里面

处理完这三个循环之后,就把这些信息封装成JavaBeanInfo返回出去,接着根据JavabeanInfo来创建并返回JavaBean反序列化器

1726111142_66e25da67f00b8572c68d.png!small?1726111142020

1726111334_66e25e66d6e39fb70ad4e.png!small?1726111334210

一路返回,我们回来到这里

1726111356_66e25e7c93d6646d3dcdf.png!small?1726111355934

我们跟进deserialze函数,看看是怎么反序列化的,发现只有值的变化,具体的代码执行调试不了,因为asmEnable这个按钮是开着的,我们返回的是asm临时创建的反序列化器

1726111380_66e25e94df0bd595f1b7f.png!small?1726111380331

我们要想要调试,那么就需要返回JavaBeanDeserializer,也就是要把asmEnable的值改成flase,我们可以在反序列化前修改一下配置,修改asmEnable的值

1726111394_66e25ea252aff7456344a.png!small?1726111394004

可以调试后,我们跟进deserialze函数,过掉一些if后,这里把我们前面加到FiledList里面的字段拿到

1726111408_66e25eb02ad2c27cc3fbc.png!small?1726111407851

然后这里构造对象

1726111415_66e25eb7632494b868da5.png!small?1726111414760

1726111420_66e25ebce3ed59088bc16.png!small?1726111420239

继续往下走调用赋值函数

1726111426_66e25ec25d24e35ceb463.png!small?1726111426145

然后里面呢,就会调用到对应setter函数和相应的只有getter的函数,所以我们一开始看到的调用parseObject函数会把setter函数给调用了,同时这里我们还知道,对于只有getter的函数,也是在返回person对象的过程中调用的

1726111460_66e25ee40a15ab7a9aee7.png!small?1726111459374


1726111468_66e25eecc3b75f41e7cb7.png!small?1726111468214

而对于有对应setter函数的getter函数,而是在返回person对象之后,调用toJSON函数调用的,也就是下面这个地方,所以调用parse()时并不会自动调用到setter函数

1726111501_66e25f0ddaebd5e8a96eb.png!small?1726111508181


我们编写一个恶意类来试试

1726111538_66e25f3233a7b12db657b.png!small?1726111537623

1726111567_66e25f4f1e488f8479108.png!small?1726111566569

可以看到弹出来计算器了,同时我们注意到,JSON字符串中存在类里面没有setter/getter函数对应的字段或者不存在的字段也是可以的,因为前面我们知道它会在setter/getter函数的处理来截取后面的部分作为字段,所以传入的JSON字符串的变量名用的是setter/getter函数的后面部分,值才会传进对应的stter/getter函数。我们实际利用中大概率也不会直接能在stter函数里面找到恶意代码,通常是以setter/getter函数为入口点,往下找反序列化的链子。






# web安全 # 漏洞分析 # Fastjson反序列化漏洞
本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者
文章目录