MySQL蜜罐实现
原理
MySQL中的load data infile
命令可以读取文件并将其内容导入到表中,通过这个命令可以读取到服务器端或者客户端的文件,然后把内容存入表中。前提是当前用户有读取文件的权限。
1 | load data local infile '/etc/passwd' into table test fields terminated by '\n'; |
local
从客户端读取文件,不指定则从服务器端读取文件/etc/passwd
指定文件test
指定写入的表
正常情况下,Client想要将本地中的test文件数据插入表中,向Server发起load data local infile 'test.txt' into table table_name
命令,Server收到请求命令后向Client索要此文件内容,Client收到之后便开始向Server发送文件。
可以看到Server和Client是对话的方式进行通信,而比较有意思的是Client并不会记录上一次请求的信息,而是第一次请求之后按照Server响应的内容进行下一步的动作,所以可以在Server端构造响应内容来对Client进行欺骗。load data local infile
来读取本地文件的过程中,首先Client向Server发出文件插入表的请求,然后Server会向Client请求test
文件的内容,如果将Server响应的信息进行修改,就可以让Server获取到自己想要的文件。
接下来通过简单分析MySQL协议的数据包来了解伪造恶意MySQL服务器的大致原理。
- 服务端:192.168.73.141
- 客户端:192.168.73.1
三次握手,建立连接
服务端–>客户端发送初始化包
客户端–>服务端发送认证包
服务端–>客户端发送结果包
当客户端和服务端完成三次握手之后,服务端会向客户端发送packet,其中包括了协议版本、数据库版本等等。
客户端收到服务端初始化数据包之后回复认证包,可以看到里面含有Username
和Password
等信息。
服务端接受到认证之后,会向客户端回复其认证结果。
以上就是MySQL建立连接的过程,认证成功之后,就可以和服务端进行交互。
向服务端发送命令,服务端返回结果:
这里重点分析load data infile
命令执行的过程,首先客户端发起load data infile
命令。
然后服务端响应,将这个文件名字给客户端。
客户端将文件内容发送给服务端。
服务端返回成功。
以下是完整交互过程,绿色的为查询表中是否存在flag.txt
中的文件信息,可以看到在交互过程中服务端向客户端响应之后,客户端将响应的文件内容发送给服务端。
实现
服务端和客户端建立连接的过程是固定的,首先监听3306端口,等待连接建立,如果有人连接,则返回初始化包,这时如果对方发送认证包,这里直接向对方返回成功请求。所以初始化包和结果包的返回信息直接复制到代码中去。
初始化包:
1 | get_version =b"\x4a\x00\x00\x00\x0a\x35\x2e\x37\x2e\x32\x36\x00\x06\x00\x00\x00\x2f\x37\x13\x0c\x6f\x39\x3b\x72\x00\xff\xf7\xc0\x02\x00\xff\x81\x15\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0f\x2b\x73\x18\x33\x58\x1a\x19\x5f\x6c\x38\x31\x00\x6d\x79\x73\x71\x6c\x5f\x6e\x61\x74\x69\x76\x65\x5f\x70\x61\x73\x73\x77\x6f\x72\x64\x00" |
结果包:
1 | resp = b"\x07\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00" |
重点在于服务端向客户端响应文件的数据包,即构造向客户端要求返回指定文件内容的数据包。Packet Length
:0b 00 00
01 fb
固定不变
内容:44 3a 66 6c 61 67 2e 74 78 74
这里的Packet Length
值实际上就是文件长度+1,因为MySQL协议中的字符串表示以NULL字节(\0)作为结尾,所以需要+1
。
python实现脚本如下:
1 | import socket |
参考链接