mysql客户端伪造以读取任意文件
先用wireshark抓取mysql流量看看整个交互过程
如下为连接时的流量包,这个过程是由客户端向服务端发起连接请求,即
mysql -h 192.168.1.5 -u xxx -p
注意这里要加上以下参数才可以实现从本地读取文件加入表,即load data local infile
中的local,否则是读取的服务端的文件。
--enable-local-infile
注意,mysql默认是只能本地访问的,远程连接是需要授权的,如以下三种方法:
-
允许所有主机使用 用户名:user 密码:password 的账户登录
GRANT ALL PRIVILEGES ON *.* TO 'user'@'%'IDENTIFIED BY 'password' WITH GRANT OPTION;
-
仅允许特定主机用 用户名:user 密码:password 的账户登录
GRANT ALL PRIVILEGES ON *.* TO 'user'@'192.168.1.12'IDENTIFIED BY 'password' WITH GRANT OPTION;
-
直接在本地mysql控制台里将mysql数据库user表host列中的地址给改掉,改成
%
即允许所有主机访问,改成特定地址则只允许特定地址主机访问。
该表用于存储mysql的用户信息。建议还是用上面的命令修改,毕竟效果是一样的。
来看流量包:
先是tcp三次握手,三个tcp协议流量包
然后是服务端向客户端发送mysql握手包。
客户端接收后将用户名,密码发给服务端,服务端再进行确认。
然后客户端向服务端发起两次request query请求,分别请求一些服务器的基本信息
就算是连接上了
再看一个普通的请求过程:
客户端发送一个request query请求
服务端执行命令并携带内容返回。
然后是load data local infile
部分,下为交互过程:
由客户端向服务端发起request query请求,附上想要插入表的文件的地址
然后服务端返回一个response TABULAR请求向客户端请求插入该地址的文件
客户端再发送数据包携带该文件内容发送给服务端。
服务端返回一个response OK告诉客户端执行完成。
但其实从师傅们的博文和官方文档中可以看到
只需要在客户端发起请求后回复一个文件传输请求就可用了。
于是整个伪造流程为
伪造服务端向客户端发送greeting握手包 ->客户端发送用户名密码登录 ->伪造服务端发送response OK确认登录成功(所以用户名密码不需要正确) ->客户端发送查询请求 ->伪造服务端接收查询请求并返回response TABULAR携带要获取文件地址发起文件传输请求 ->客户端发送文件内容 ->伪造服务端发送response OK告诉客户端完成请求
自己写了个脚本,但是完成不了。
能成功发送握手包且发送确定登录包,但是当我发送文件读取流量包时候就有了问题
报错说的是版本不匹配,但我并没在正常流程wireshark中发现包中带有版本信息。(感觉是中间那个请求服务端环节的问题。不是很懂)
然后用了大佬的poc,能够复现成功。
又抓了一遍流量包,注意到一个点,在连接成功后客户端向服务端发起查询请求以获得初始化数据的时候,那个请求也是可以返回一个文件传输请求完成文件读取的
所以上面自己那个脚本真的很迷xxxx理论上就按照那个流程发请求应该就能成功才对…
另外我连接的时候也没用--enable-local-infile
,也实现从本地读取文件了,迷惑。
看了大佬的博客,这玩意可以拿来放vps上钓各种扫描器。感觉还挺有意思的,想试试!
有关这个的题目是ddctf 2019中的mysql弱口令和2019国赛题。本来想搭环境复现…都没找到源码,国赛题甚至连题都没找到。迷惑
学习自以下博文和mysql官方文档(中文文档属实拉跨,啥都没有,还不如谷歌翻译看原文档!):
https://lightless.me/archives/read-mysql-client-file.html#_label3_0
https://www.freebuf.com/vuls/188910.html
https://dev.mysql.com/doc/refman/8.0/en/load-data-local-security.html