恶意MySQL Server读取MySQL Client端文件

据说最早2013年在俄国论坛出现。大意是,MySQL Server有机会指示MySQL Client主动上传指定文件,细节参看[1]、[2],写得很清楚,建议先看完它们再继续。

背景介绍

据说最早2013年在俄国论坛出现。大意是,MySQL Server有机会指示MySQL Client主动上传指定文件,细节参看[1]、[2],写得很清楚,建议先看完它们再继续。

很少关注入侵技术,这个洞出来这么多年,看CVE-2019-12086时才知道MySQL还有这么一个洞。本文是一些学习笔记。

搭建测试环境

得安装MySQL Server,以便抓包观察。

安装MySQL 5.7.28

在RedHat 7.6上安装MySQL 5.7.28。

查看开机自启动的mysql相关服务:

禁止mysqld.service开机自启动:

为什么禁止自启动?因为我经常用这台RedHat 7.6,平时也用不上MySQL。

手工启动MySQL:

查看MySQL状态:

寻找MySQL的root口令:

安全加固:

按提示输入之前那个临时root口令,重新设置root口令。

重启MySQL:

优化测试环境

MySQL Server有一些缺省安全机制,比如root不能远程登录,口令强度有限制,不能太弱。对于正常使用来说,它们有益,对于测试来说,就显得不便。我需要root远程登录,我想设123456这种口令。

取消弱口令限制:

修改root口令为123456,允许root远程登录:

创建测试表

测试完毕后可以删除之:

Windows上的MySQL Client

https://dev.mysql.com/downloads/mysql/
https://dev.mysql.com/get/Downloads/MySQL-8.0/mysql-8.0.18-winx64.zip

为了绿色使用,至少得三个文件:

libcrypto-1_1-x64.dll
libssl-1_1-x64.dll
mysql.exe

LOAD DATA LOCAL INFILE

在Linux上测试正常用法

在Windows上测试正常用法

实测时提示:

ERROR 1148 (42000): The used command is not allowed with this MySQL version

$ mysql.exe -V
mysql Ver 8.0.18 for Win64 on x86_64 (MySQL Community Server – GPL)

Windows上MySQL Client 8.0.18缺省不允许”LOAD DATA LOCAL INFILE”,需要显式允
许,下面两种方式均可:

用Wireshark抓包观察通信协议

如果直接抓上面这条命令,会发现MySQL Client很快就进行SSL协商,后续通信全在SSL保护下。我们的目的是观察通信协议,可以关闭SSL保护:

Summary

下面抓取报文的Summary:

TCP三次握手后,MySQL Server主动发送”Server Greeting”(4号报文)。5号报文开始登录认证,9号报文指明登录成功。10号报文是MySQL Client缺省发出的”select @@version_comment limit 1″,11号报文是其响应。14号报文是”LOAD DATA LOCAL INFILE”,15号报文是所谓的”Response TABULAR”,16号报文是MySQL Client向MySQL Server上传文件。

Server Greeting

Login Request

登录成功的响应

select @@version_comment limit 1

LOAD DATA LOCAL INFILE

Response TABULAR

上传文件

win.ini内容如下:

“幺蛾子”所在

MySQL Client从来自MySQL Server的”Response TABULAR”(15号报文)中获取文件名,然后在16号报文中上传相应文件。MySQL Client主动发出的14号报文就是个摆设。更要命的是,如果MySQL Client允许”LOAD DATA LOCAL INFILE”,当它收到来自MySQL Server的”Response TABULAR”时,不管自己是否主动发起过上传文件的请求,都会上传文件。

前面抓取的报文中,MySQL Client会主动”select @@version_comment limit 1″,如果远端是恶意MySQL Server,可以直接响应以”Response TABULAR”,MySQL Client将立即上传文件,不需要其他交互。

rogue_mysql_server.py

已经有人写了恶意MySQL Server,参[3],网上有三个版本:

rogue_mysql_server_Gifts.py // 2013版本,已不适用较新的MySQL Client
rogue_mysql_server_allyshka.py // 2016版
rogue_mysql_server_jas502n.py // 2019年中国人改的

它们本名都是rogue_mysql_server.py,加上作者名以便叙述。

rogue_mysql_server_Gifts.py

有几篇讲CVE-2019-12086的文章给的链接就是rogue_mysql_server_Gifts.py,但我用这个版本死活不能读取MySQL Client端文件。

在Linux上:

在Windows上:

MySQL Client访问恶意MySQL Server时提示”Malformed packet”。

这次MySQL Client不必指定”–ssl-mode=DISABLED”。通信过程是否启用SSL由Server和Client协商后决定,”Server Greeting”中”Server Capabilities”字段有:

“Login Request”中”Client Capabilities”字段有:

只有这两个bit同时置位才会启用SSL。rogue_mysql_server.py的”Server Greeting”中”Server Capabilities”字段没有启用SSL,此时MySQL Client发出的
“Login Request”中”Client Capabilities”字段也不会启用SSL。即使MySQL Server启用SSL,MySQL Client仍可通过”Login Request”中”Client Capabilities”字段表明己方不支持SSL,这跟本文主旨无关,就是提一句。

过于陈旧的”Server Greeting”

抓包观察:

如果用CVE-2019-12086的PoC测试,可能得到错误提示:

MySQL Client收到上面这个”Server Greeting”之后判定其格式非法,主动关闭了TCP连接。最初尝试复现CVE-2019-12086失败,并未抓包细究,后来才发现这茬。

Gifts提供的”Server Greeting”如下:

从注释看,应该是抓”5.1.66-0+squeeze1″版的通信报文后修改而得。这个太老了,已不适用较新的MySQL Client,需要将”Server Greeting”修改如下:

Gifts的第二个Salt是8+1=9字节,对于较新的MySQL Client,该字段必须是12+1=13字节。此外必须让”Extended Server Capabilities”中如下位置位:

修改后的”Server Greeting”

rogue_mysql_server_allyshka.py

相比Gifts版,allyshka版修改了三处:

  1. 将log.setLevel(logging.DEBUG)改成logging.INFO
  2. 修改”Server Greeting”
  3. 注释掉对daemonize()的调用

真正起作用的是第2项修改。

rogue_mysql_server_jas502n.py

相比allyshka版,jas502n版修改了一处:

  • 通过命令行参数指定待获取的文件

漏洞复现

在Linux上:

在Windows上:

提示输入口令时直接回车,恶意MySQL Server对任意user:pass都响应以成功登录;然后MySQL Client主动向恶意MySQL Server发送win.ini,无需其他交互。若无”–enable-local-infile”,MySQL Client不会主动上传文件,说明这个版本的MySQL Client默认禁止”–enable-local-infile”。

MySQL Client允许”–enable-local-infile”体现在”Login Request”的”Client Capabilities”中:

观察此时”Login Request”的”Client Capabilities”:

该位复位表示MySQL Client禁止”–enable-local-infile”,这种情况下无法复现漏洞,mysql.exe收到”Response TABULAR”后不会进一步发送响应报文。

CVE-2019-12086

PoC

MySQL Connector/J 8.0.14产生的”Login Request”

下面是mysql-connector-java-8.0.14.jar产生的报文:

“Client Capabilities”中的”Can Use LOAD DATA LOCAL”默认置位。

MySQL Connector/J 8.0.14产生的初始查询

mysql.exe的初始查询是”select @@version_comment limit 1″,mysql-connector-java-8.0.14.jar的初始查询不一样。

MySQL Connector/J 8.0.14自动发起的查询

如果恶意MySQL Server的状态机实现得够鲁棒,会发现mysql-connector-java-8.0.14.jar自动发送4次”Request Query”:

发出”SET NAMES latin1″后,未对”Response TABULAR”产生动作。而其他3次”Request Query”均对”Response TABULAR”产生动作,也就是上传了3次文件。

MySQL Connector/J 8.0.15的修补方案

参[4]

检查com.mysql.cj.conf.PropertyDefinitions,其中有一句:

8.0.14中allowLoadLocalInfile默认为true,8.0.15中该值默认为false,相当于没有指定”–enable-local-infile”。

抓包观察8.0.15登录认证报文的”Client Capabilities”,会看到:

防御措施

参[5],官方给的安全建议。

rogue_mysql_server.py不支持SSL,如果MySQL Client强制启用SSL,中招可能性降低。不排除新的恶意MySQL Server出现,不要依赖这点进行防御。

MySQL Client指定”–ssl-mode=REQUIRED”后,如果”Server Greeting”中
“Server Capabilities”字段没有启用SSL,MySQL Client报错退出。

这是个协议设计层面的安全漏洞,MySQL Client发出”Request Query”,如果不是”LOAD DATA LOCAL INFILE”,就不应该对”Response TABULAR”产生动作,状态机太不严谨。现状是只要MySQL Client发出”Request Query”,就会对”Response TABULAR”产生动作。至今没有从状态机层面修补漏洞,而是缺省禁止上传文件,这算什么修补?

杂项

蜜罐

这个很好理解,在蜜罐中布署恶意MySQL Server,等待对3306/TCP的扫描尝试。万一扫描器使用了存在漏洞的MySQL Client封装,就会被蜜罐反向捕捉敏感信息。

通过社会工程学诱使他人前来访问恶意MySQL Server也是一种选择,在github上故意泄露恶意MySQL Server的user:pass,在论坛发布渗透测试的文章,等等。

网上公开的源自Gifts的恶意MySQL Server,其状态机过于简陋,鲁棒性、兼容性、功能性均属于PoC级别。提供一些设计思路供参考:

  1. 实现鲁棒性较强的状态机,兼容不同MySQL Client行为
  2. 直接将捕获的客户端文件保存成单个文件,文件名以客户端IP结尾,便于区分
  3. 支持二进制文件上传
  4. 支持上百MB大文件上传
  5. 尽可能捕获客户端信息,捕捉登录请求、查询语句、退出命令等
  6. 支持向日志文件输出Console日志
  7. 支持可控daemon化
  8. 命令行指定待捕获的客户端文件
  9. 允许命令行指定侦听的IP、PORT
  10. 允许命令行指定banner
  11. 考虑消耗内存、消耗硬盘等DoS攻击

扫描恶意MySQL Server

100%有人在公网布署恶意MySQL Server,其不可避免地会被扫描识别出来。提供一些设计思路供参考:

  1. 检查”Server Greeting”是否可疑,比如是否禁用SSL、允许上传文件
  2. 检查”Login Response”是否可疑,比如明显不正确的user:pass直接登录成功
  3. 检查普通”Request Query”是否也收到”Response TABULAR”,显示文件名信息

这个扫描过程务必自己用socket编程实现,不要调用现成的MySQL Client封装,以免被反搞。

参考资源

[1] MySQL connect file read – [2016-04-20]
http://russiansecurity.expert/2016/04/20/mysql-connect-file-read/

[2] Read MySQL Client’s File – [2018-09-06]
https://lightless.me/archives/read-mysql-client-file.html

[3] https://github.com/Gifts/Rogue-MySql-Server
(2013版本,已不适用较新的MySQL Client,需要修改)

https://github.com/allyshka/Rogue-MySql-Server
(这是修改过的可用版本)

https://github.com/jas502n/CVE-2019-12086-jackson-databind-file-read
(这是另一个修改过的可用版本)

[4] http://central.maven.org/maven2/mysql/mysql-connector-java/8.0.14/mysql-connector-java-8.0.14.jar
http://central.maven.org/maven2/mysql/mysql-connector-java/8.0.15/mysql-connector-java-8.0.15.jar
http://central.maven.org/maven2/mysql/mysql-connector-java/8.0.15/mysql-connector-java-8.0.15-sources.jar

[5] Security Issues with LOAD DATA LOCAL
https://dev.mysql.com/doc/refman/8.0/en/load-data-local.html
(官方给的安全建议)

发表评论