追洞小组 | Jdbc反序列化漏洞复现浅析
目錄
?
1、前言+靶場搭建?
2、漏洞復現?
3、漏洞分析
4、漏洞修復?
5、心得
?
?
前言+靶場搭建
?
很多時候我們獲得密碼之后進入后臺管理的界面,有些上傳的漏洞或者sql注入無法getshell,但是如果發現連接mysql服務的數據包中可以傳參,那么我們就可以嘗試控制鏈接mysql服務器,反序列化代碼來得到shell。所以該漏洞的關鍵只需要能夠控制客戶端的jdbc連接,在連接階段就可以進行觸發反序列化。這篇文章也不深入理解mysql的協議。使用idea maven項目創建,在pom中導入jdbc的坐標。(5版本的在5.1.40以下,8版本的在8.0.20以下)導入之后在右邊點擊maven圖標導入。
需要自行根據版本選擇JDBC連接串,最后有基于各版本Connector連接串的總結。
public class test1 {public static void main(String[] args) throws Exception {String driver = "com.mysql.jdbc.Driver"; //8版本是"com.mysql.cj.jdbc.driver"//String DB_URL = "jdbc:mysql://127.0.0.1:3306/test?autoDeserialize=true&queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor&yso_CommonsCollections5_calc"; //8.x使用String DB_URL = "jdbc:mysql://127.0.0.1:3306/test?detectCustomCollations=true&autoDeserialize=true&user=yso_CommonsCollections5_calc"; //5.x使用Class.forName(driver);Connection conn = DriverManager.getConnection(DB_URL);} }漏洞復現
一、 準備偽造的mysql服務
MySQL服務器使用:https://github.com/fnmsd/MySQL_Fake_Server
二、 可控的mysql連接的字符串
可以去找到"靶場"。這里當然以本地靶場搭建。
漏洞分析?
?
detectCustomCollations觸發方式
測試環境中使用mysql-connector-java 5.1.32+java 1.8.221:
我們在 com.mysql.jdbc#buildCollationMapping()下上斷點,初始化了一個Map indexTocharset;并且if判斷為false再進入下一個if體。
關鍵的語句在藍色的那一行。前提條件是紅框判斷jdbc的版本大于4.1.0,然后執行show collation語句,再判斷版本大于5.0.0,才將show collation的執行結果results傳入的Util的resultSetToMap執行。
進入
跟入com.mysqljdbc.result.ResultSetImpl#getObject可以看到反序列化的觸發點,因為剛剛傳入的參數是2和3,所以只要這兩個字段的sqlType屬性為-4,那么我們就可以進入反序列化的入口。
sqlType為-4,一直進入到-2的判斷。這里我們可以看到field.mysqlType不能為255,并且field是Binary而且是Blob屬性才能進入獲取字段的data屬性,并進行之后的反序列化。
ServerStatusDiffInterceptor觸發方式
?
測試環境中使用mysql-connector-java 8.0.14+java 1.8.221:
這部分有點長,解釋一下為什么會執行四次命令。
queryInterceptors:一個逗號分割的Class列表(實現了com.mysql.cj.interceptors.QueryInterceptor接口的Class),在Query"之間"進行執行來影響效果。(效果上來看是在Query執行前后各插入一次操作)autoDeserialize:自動檢測與反序列化存在BLOB字段中的對象。
所以如上所述,如果要觸發queryInterceptors則需要觸發SQL Query,而在getConnection過程中,會觸發SET NAMES utf、set autocommit=1一類的請求,所以會觸發我們所配置的queryInterceptors。
com.mysql.cj.protocol.a.NativeProtocol#invokeQueryInterceptorsPre方法會調用ServerStatusDiffInterceptor的preProcess方法,再去調用了populateMapWithSessionStatusValues,以下是調用鏈:
首先先大致說一下為什么會執行四次命令
接下來我們細分一下到底查詢了什么之后的細分步驟。
首先連接mysql服務器,并ConnectionImpl中設置客戶端的字符集,我們進入這個方法。因為前面那個方法的charsetEncoding為空值,所以進入這個方法查詢如何配置。
在NativeSession.class里會獲取當前mysql的環境然后會觸發一次查詢"SET NAMES utf",并發送該請求,在python中收到請求。
走完之后完成了下斷點的這句,緊接著走箭頭所指向的這句話。還在com.mysql.cj.jdbc.ConnectionImpl這個類中。
隨后會調用com.mysql.cj.protocol.a.NativeProtocol類中的sendQueryString->sendQueryPacket里面再是sendCommand->send方法,有興趣可以跟一下),在這里我們可以看到這個方法將setautocommit=1傳入invokeQueryInteceptorsPre()方法中。而進入這里的方法只是將上次的set namesutf8的結果返回并反序列化中,而進入這里的方法只是將上次的set namesutf8的結果返回并反序列化。
而在上述的利用鏈中我們可以得知在com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor中的preProcess及postProcess都會觸發populateMapWithSessionStatusValues這個方法,而這個方法是反序列化readObject方法的前置出發點。
一直走到反序列化的點,將結果返回后反序列化,彈出第一次計算機。
resultSetToMap
getObject的反序列化方法
剛剛將set names utf8并結果返回的show session status invokeQueryInterceptorPre走完,到
再到,可以看到調用了postProcess執行反序列化操作
postProcess走完,控制臺打印
python
至此第一部分結束。
第二部分流程也是類似的情況,就不列舉了:
都是在第二次的show Session Status進行了反序列化的操作。剛剛是分析了第一個紅框的兩次反序列化操作,接下來是下一個紅框的反序列化操作,可以看到左下角的調用棧。
隨后服務器因為執行了SHOW SESSION STATUS會觸發一次preProcess()
進而在觸發SET sql_mode='STRICT_TRANS_TABLES'
查詢然后進入NativeProtocol#sendQueryString觸發一次postProcess
postProcess中最后打印臺的兩句話印證了我們的思路并且將結果返回。
漏洞修復
?
queryInterceptors參數是mysql-connector-java-8.0.7版本才開始支持的,21版本以上被修復。
取消了反序列化的操作,getObject改成了getString。
心得
?
這個也是在反序列化的過程中沒有對數據做嚴格的檢驗導致的,利用起來的話,反序列化的操作還是需要環境有可利用的類,所以這個漏洞有可能利用起來很雞肋,但是如果能夠找到這樣一個控制mysql字符串的地方,不妨可以試一試。
?
總結
以上是生活随笔為你收集整理的追洞小组 | Jdbc反序列化漏洞复现浅析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 干货|SRC漏洞挖掘经验
- 下一篇: WAF机制及绕过方法总结:注入篇