最新消息:

Read MySQL Client’s File

渗透流程与思路 admin 64浏览 0评论

这应该是一个比较老的问题了,前几天看到其他人聊到这个问题,拿出来分析一下。

简单的讲,就是我们可以伪造一个 MySQL 的服务端,甚至不需要实现 MySQL 的任何功能(除了向客户端回复 greeting package),当有客户端连接上这个假服务端的时候,我们就可以任意读取客户端的一个文件,当然前提是运行客户端的用户具有读取该文件的权限。

在复现的时候,有些地方踩到了一些坑,原作者没有说明,特此记录一下。

0x00 LOAD DATA INFILE

这个问题主要是出在LOAD DATA INFILE这个语法上,这个语法主要是用于读取一个文件的内容并且放到一个表中。通常有两种用法,分别是:

load data infile "/data/data.csv" into table TestTable;
load data local infile "/home/lightless/data.csv" into table TestTable;

有时候也会与FIELDS TERMINATED BY '\n'一起使用,效果更佳。这两种用法的区别就是差了一个local,第一个 SQL 语句的意思是,读取服务器上/data/data.csv文件,并写入到TestTable中;第二个 SQL 语句的意思则是,读取本地(客户端)这边的/home/lightless/data.csv文件,并写入到TestTable中。而我们这次要利用的也就是LOAD DATA LOCAL INFILE这种形式。

通过查阅 MySQL 的官方文档,在文档上,官方也指出了这个语法的问题:

那么接下来,我们来看下如何构造一个恶意的 MySQL 服务端来读取客户端的文件。

0x01 Capture The Package

如果想构造这样的服务端,首先要搞明白 MySQL 的交互流程,测试环境如下:

  • Ubuntu 18.04
  • MySQL 官方 APT 仓库安装的 5.7.23

通过 Wireshark 抓取 3306 端口的数据包,一开始发现 MySQL 的数据包全部走了 SSL 通信。

仅有一开始的两个 MySQL 的包,后面全是 SSL 数据了,这样也就没法搞明白是怎么读客户端文件的了。我们需要把 SSL 关掉,只需要关闭 MySQL Client 端的 SSL 即可,在配置文件的[mysql]节添加skip_ssl(或者关掉服务端的 SSL 支持也可以):

这样 Wireshark 中就可以抓到正常的数据包了。会发现一共有这样几步:

  1. greeting 包,获取服务端的 banner;
  2. 登录请求包;
  3. 初始化的一些查询,比如select @@version_comment limit 1之类的;

接下来我们找到关键的LOAD DATA INFILE数据包,第一个包看起来比较正常,是客户端发起的Request Query,如果你无法使用LOAD DATA INFILE语法的话,考虑在连接 MySQL 的时候加上--enable-local-infile选项,或者设置local_infile全局变量为ON

但是紧接着,服务端回复了一个包含刚刚请求中文件名的包,这里把/etc/passwd又给发回去了:

然后客户端向服务端发送了/etc/passwd文件的内容:

看到这里,也许开始有点想法了,如果我们在客户端发送查询之后,返回一个Response TABULAR数据包,并附上我们指定的文件,是不是就可以读取客户端的文件了,答案是肯定的。正如官方文档中提出的安全风险,”In theory, a patched server could be built that would tell the client program to transfer a file of the server’s choosing rather than the file named by the client in the LOAD DATA statement.”,可以看到,客户端读取哪个文件其实并不是自己说了算的,是服务端说了算的,形象一点的说就是下面这个样子:

  • 客户端:hi~ 我将把我的 data.csv 文件给你插入到 test 表中!
  • 服务端:OK,读取你本地 data.csv 文件并发给我!
  • 客户端:这是文件内容:balabal!

正常情况下,这个流程不会有什么问题,但是如果我们制作了恶意的客户端,并且回复服务端任意一个我们想要获取的文件,那么情况就不一样了。

  • 客户端:hi~ 我将把我的 data.csv 文件给你插入到 test 表中!
  • 服务端:OK,读取你本地的 / etc/passwd 文件并发给我!
  • 客户端:这是文件内容:balabal(/etc/passwd 文件的内容)!

看到这里可能有同学会问,“即便可以读任意文件,那也要等到客户端发起 LOAD DATA INFILE” 的时候才能回复呀!如果你刚才仔细阅读官方文档上的安全风险的话,会发现有这么一句:”A patched server could in fact reply with a file-transfer request to any statement, not just LOAD DATA LOCAL”

伪造的服务端可以在任何时候回复一个 file-transfer 请求,不一定非要是在LOAD DATA LOCAL的时候。这里有一点要说明的是,如果想要利用此特性,客户端必须具有 CLIENT_LOCAL_FILES 属性,这一点也会在下文提到的官方数据包格式文档中有所说明,这也是为什么在前面测试的时候需要添加--enable-local-infile的原因。

0x02 Pseudo Server

那么我们现在可以开始制作我们的服务端了,主要是以下几个步骤:

  1. 向 MySQL Client 发送Server Greeting
  2. 等待 Client 端发送一个Query Package
  3. 回复一个file transfer请求

现在要解决的就是两个问题,分别是Server Greeting包和file transfer包的格式,不过也不难,这些包的格式都可以在 MySQL 的官方文档上找到,首先来看File Transfer的包格式,Protocol::LOCAL_INFILE_Request,这幅图也可以发现,我们需要等待一个来自 Client 的查询请求,才能回复这个读文件的请求

官方文档上还贴心的准备了一个 Example:

0c 00 00 01 fb 2f 65 74    63 2f 70 61 73 73 77 64    ...../etc/passwd

数据包的内容其实是从\xfb开始的,这个字节代表包的类型,后面紧跟要读取的文件名。前面的0x0c是数据包的长度(从 \ xfb 开始计算),长度后面的三个字节\x00\x00\x01是数据包的序号。

同理,我们很容易就能根据这份文档获取Greeting的数据包结构。如果感觉自己伪造比较困难的话,可以直接提取前面通过 Wireshark 抓到的包,修改一下长度、文件名之类的字节即可。

这里给一个Greeting包的样例,每个字段的作用均已表明,参考文档不难理解。

'\x0a',  # Protocol
'6.6.6-lightless_Mysql_Server' + '\0',  # Version
'\x36\x00\x00\x00',  # Thread ID
'ABCDABCD' + '\0',  # Salt
'\xff\xf7',  # Capabilities, CLOSE SSL HERE!
'\x08',  # Collation
'\x02\x00',  # Server Status
"\x0f\x80\x15", 
'\0' * 10,  # Unknown
'ABCDABCD' + '\0',
"mysql_native_password" + "\0"

接下来就是动手写 PoC 了,如果不想自己动手,可以参考 “参考文档” 中的 PoC。

一开始构造好的时候,用客户端去连接,总是提示 ERROR 2026 (HY000): SSL connection error: protocol version mismatch 或者 ERROR 2027 (HY000): Malformed packet。如果你在构造 PoC 的时候也出现了这种问题,很大可能是握手包构造的有问题,对照文档仔细检查。客户端默认是支持 SSL 的,服务端需要告诉客户端服务端不支持,我通过两次分别抓 “服务端支持 SSL” 和“服务端不支持 SSL”的包进行比对,再参考文档,找到了关闭 SSL 的方法,就是Capabilities这两个字节,\xf7 则表示不支持 SSL,如果不明白,抓个包比对一下就懂了。

仔细观察下图中的各种字段,避免踩坑。

0x03 Exploit it!

a. MySQL Client

使用 MySQL Client 进行测试,效果拔群:

b. PHP with mysqli

PHP通过mysqli连接恶意数据库,效果拔群:

c. PHP with PDO

默认未开启MYSQL_ATTR_LOCAL_INFILE属性,需要手工开启才可以以。

d. Python with MySQLdb

原作者说无效,但是我测试了一下,效果拔群。

e. Python3 with mysqlclient

效果拔群。

f. Java with JDBC Driver

效果拔群。

z. Others

其他语言各位可以自行测试。

0x04 Defense And Thinking

对于这种攻击的防御,说起来比较简单,首先一点就是客户端要避免使用 LOCAL 来读取本地文件。但是这样并不能避免连接到恶意的服务器上,如果想规避这种情况,可以使用--ssl-mode=VERIFY_IDENTITY来建立可信的连接。

当然最靠谱的方式还是要从配置文件上根治,关于配置上的防御问题,可以参考这篇文档进行设置。

用途的话,做蜜罐是肯定可以的,但是受众面好像不太完整,各种语言的支持都不太一样,比如 Python 中的 MySQLdb 包,原作者说这个包不受影响,但是我测试的时候确是受影响的,而 PHP+PDO 的方式又不受影响,所以能否中招还是要靠一些运气。

而且这些语言中的三方包在连接MySQL的时候,基本上在连接成功之后都会发出一下SELECT语句来查询一些版本号、编码之类的数据,这就达成了前面提到过的第二个攻击条件:等待客户端发来一个QUERY请求,所以如果这些第三方包允许执行LOAD DATA INFILE,危害还是比较大的。

0xFF 参考文档

 

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

参考资料:https://www.freebuf.com/vuls/188910.html

转载请注明:jinglingshu的博客 » Read MySQL Client’s File


Warning: Use of undefined constant PRC - assumed 'PRC' (this will throw an Error in a future version of PHP) in /usr/share/nginx/html/wp-content/themes/d8/comments.php on line 17
发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址