CVE-2022-31197 PostgreSQL JDBC SQL注入分析 CVE-2022-31197 PostgreSQL JDBC SQL注入分析

CVE-2022-31197 PostgreSQL JDBC SQL注入分析

写在前面

偶然看到了CVE-2022-31197,是由于ResultSet.refreshRow()引发的SQL注入,感觉有点小有意思,正好之前学习了JDBC attack,决定分析一下漏洞造成的原因

漏洞分析

在官方的描述中,被修复版本有42.2.26 42.4.1

这里我们选用42.2.23版本的postgresql数据库依赖

<dependency>
    <groupId>org.postgresql</groupId>
    <artifactId>postgresql</artifactId>
    <version>42.2.23</version>
</dependency>

在连接中,他给出了一个payload

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

这个cve的漏洞点主要是在PgResultSet#refreshRow方法中,在该方法中打下断点,跟进代码

在这个方法中我观察到有一处执行sql语句的地方,或许那里就是漏洞触发点吧?

言归正传,如果我们需要到达漏洞触发点位置第一个拦路虎就是else if语句中的判断

拆分开来,第一个是需要使得this.isBeforeFirst()为false,跟进代码逻辑

其中需要使得this.rowOffset + this.currentRow < 0但是前者为0

只能从后者做文章了,那么this.currentRow是什么捏?

怎么才能使得其不为-1捏?

通过全局搜索对currentRow属性的赋值,我发现在next方法中,存在有其赋值操作

所以我们需要使得这个表有数据并在调用ResultSet.refreshRow之前调用ResultSet.next才能满足条件

回到else if语句,继续分析,isAfterLast的调用

要想返回false,因为之前需要表中存在数据,所以if语句就不能返回false, 我们就只能使得currentRow属性小于rows_size才能满足条件

接下来就是Nullness.castNonNull(this.rows, "rows")使得其返回不为空,跟进

很简单,前面就已经满足了,只需要满足有数据就OK

接下来就是sql语句的拼接逻辑,简单看看,了解payload的构造原理

在最开始就创建了select开头的StringBuilder类,之后通过一个for循环获取表中的列名,并加以拼接

接下来又在后面添加了from/表名/where等关键词

之后又是获取了primary key修饰的列名,并且在后面添加了= ?这类预编译手法,如果有多个primary key,将添加and逻辑词处理

接着就在最后调用了查询语句,执行了恶意SQL

漏洞利用

Payload构造

从上面的分析中可以知道,在获取的列名前面加上了select column1, 所以我们首先需要闭合前面的,所以payload中的第二列名是1 from dbName;开头,值得注意的是这里使用了分号进行sql语句之间的分割,那么列名中间部分就是我们需要执行的sql语句了,同样的根据上面的分析,我们知道,在列名的后面同样加上了from dbName where id = ?,所以,在payload的最后我们需要闭合后面部分,使用select *就能成功闭合

利用

  1. docker运行postgresql数据库

  2. 远程连接,首先创建一个表

    CREATE TABLE refresh_row_example3 (
      id     int PRIMARY KEY,
      "1 FROM refresh_row_example3;CREATE TABLE test(id int);SELECT * " int
    );
    

    这里我直接创建一个表展示能够成功利用,当然还有更多的利用姿势

  3. 随便添加一组数据

  1. 运行测试程序

可以发现成功创建test表

环境代码示例上传到了github

漏洞修复

根据代码对比
https://github.com/pgjdbc/pgjdbc/commit/739e599d52ad80f8dcd6efedc6157859b1a9d637

在修复版本中,不在直接将列名写入sql语句中,而是经过了Utils.escapeIdentifier的处理

Ref

https://github.com/pgjdbc/pgjdbc/security/advisories/GHSA-r38f-c4h4-hqq2

评论 0

挤眼 亲亲 咆哮 开心 想想 可怜 糗大了 委屈 哈哈 小声点 右哼哼 左哼哼 疑问 坏笑 赚钱啦 悲伤 耍酷 勾引 厉害 握手 耶 嘻嘻 害羞 鼓掌 馋嘴 抓狂 抱抱 围观 威武 给力
提交评论

清空信息
关闭评论
sitemap