飞秋(FeiQ)3.0.0.2 远程拒绝服务漏洞审计
飞秋 3.0.0.2 在处理图片和文件的分片接收时,会用三个取自网络报文的数值做内存操作:分配大小、目的偏移、拷贝长度。这三项都没有边界校验。任意一台与目标网络可达的主机发送一个构造报文,就能造成堆越界写,进程随后以 0xC0000374(STATUS_HEAP_CORRUPTION)终止。
审计在受控实验网络内完成,目标是自己搭建的同一版本样本,没有涉及第三方资产。
| 项目 | 结论 |
|---|---|
| 漏洞类型 | 堆缓冲区溢出(CWE-787 / CWE-122) |
| 触发前提 | 无。不需要认证、不需要交互、不需要目标侧任何操作 |
| 网络条件 | 与目标网段可达即可,走 UDP 2425 |
| 打击面 | 4 个 UDP 报文,合计约 32 KB |
| 失效耗时 | 远程实测 2.9 秒 |
| 崩溃表现 | 0xC0000374,目标机事件日志可查 |
| 恢复方式 | 只能人工重启,飞秋没有自愈或自动重启机制 |
| 可否持续压制 | 可以,目标恢复后 8 秒内可再次打掉 |
样本
| 项 | 值 |
|---|---|
| 文件名 | FeiQ.3.0.0.2.exe |
| 格式 | PE32(x86),GUI 子系统 |
| 编译器 | MSVC 10.0 |
| 加壳 | 无 |
| MD5 | bfea3e87b66b4164badd346de8172a36 |
| SHA256 | 480ff41b04bd8e93ce027b33a2d8dae9531fdf9e4651c0d728e9f490c93f1ae6 |
| ImageBase | 0x400000 |
| 调试信息 | 残留 PDB 路径 F:\FeiQ2012\source\Client\FeiQClient\Win32\Release\FeiQ.pdb |
UDP 2425 是飞秋唯一不需要认证的数据入口,承载的报文类型也最多,本文的审计对象就是它。
报文格式与解析链
协议没有公开文档,是 IPMSG 的私有派生。审计从二进制里把报文的文本格式完整逆了出来,确保能手工构造合法报文。二进制里对应三条硬编码字符串:
1 | 偏移 0xEAE908 : "%d%s%d_%d#%ld#%s#%d#%d#%d#%s#%d:%ld:%s:%s:%ld:" |
由此得到线格式:
1 | <ver>_lbt<gen>_<n>#<pktno>#<mac>#<f1>#<f2>#<f3>#<cmd>#4001#9:<cmd2>:<user>:<host>:<f4>:<body> |
几个字段的含义是通过逐字段替换、观察回包变化确定的。<cmd> 是十进制,实际按位域使用,低位是命令号、高位是标志位。<user> 和 <host> 决定对端列表里的显示名。对端记录以源 IP 为键,同一个源 IP 只登记一个账户。报文尾部可以附加任意二进制数据,长度由报文总长与头部长度之差推出。
从收包到越界写的调用链见图一。
其中有一个细节在审计里容易漏掉。case -64 分支不是无条件可达的,它要求两个条件同时成立:ctx+0xD4 不为 0,也就是报文里 _lbt 后面的代号不能是 0;命令字必须带 0x200000 标志位。因此可达的命令值是 0x2000C0,而不是 0xC0。如果只按「命令字等于 0xC0」去构造报文,永远打不到这个函数。
漏洞成因
sub_6B8600 有 4800 余字节的栈帧,直接阅读很容易迷失,于是改用「先筛危险调用」的办法收敛:在函数内检索 GlobalAlloc、GlobalLock、memcpy、_wtoi 的位置,再从这些位置往上看参数来源。
筛完之后的结论是,参与内存操作的四个参数里只有一个是校验过的。
对应的代码片段如下,变量名与反编译输出一致:
1 | // ── 1. 从正文 TLV 里解析字段(字段以 '|' 分隔,末尾 '#')── |
越界写发生在最后那个 memcpy。它缺少的检查可以写成一个条件:
说人话:程序应当先确认写入起点落在分配区里、写完不越出分配区。现在这两个条件一个都没有。
dwBytes 来自 wcstoul 且没有上界,v374 是 _wtoi 出来的有符号数,Size 由报文总长决定。三个都来自网络,都没有约束。
为什么可以反复施加
唯一被校验的 v376 只用来做分片去重。去重位图位于 entry + 0x2F + seq,seq 的合法区间是 1 到 8000,因此每个不同的 seq 都会独立触发一次 memcpy,同一条连接条目上最多可以写入 8000 次,每次的偏移和长度都能不同。
所以一次攻击可以在这条连接上连续施加,每次换一个偏移,不需要重新建立连接。
崩溃机制
越界写落在堆上之后,进程不会立刻终止。观察到两种终止路径:
| 异常码 | 含义 | 触发条件 |
|---|---|---|
0xC0000374 |
STATUS_HEAP_CORRUPTION |
越界写落在已提交页内,破坏了堆块结构,堆管理器在后续的分配、释放或校验中发现并主动终止进程 |
0xC0000005 |
STATUS_ACCESS_VIOLATION |
越界写落到未提交页,或破坏了随后立即被解引用的指针 |
两者是同一处溢出的不同落点,这也解释了为什么单个报文有时能打崩、有时不能:取决于落点是否踩到了已提交且正在使用的堆结构。
多次复现的崩溃特征一致,崩溃模块为 ntdll.dll,异常码 0xc0000374,偏移 0x000fd5cf。0xfd5cf 是堆管理器的错误处理路径,可以当作堆结构被破坏的指纹。
验证
本地
在本地运行同版本样本,连续施加多个不同分配尺寸的分片写,5 次全部成功。
远程差分对照
在同一台远程靶机、同一次运行里先后做两次打击,唯一变量是发包数量。
基线探测得到 5 个回包。只发 1 个报文(8115 字节)时,目标仍存活,回包还有 4 个。发 4 个报文(约 32 KB)时,目标在 2.9 秒内失联。
同一个进程、同一条网络路径,只有包数不同,结果从存活变成宕机。这组对照排除了「目标本来就要挂了」和「报文本身打断了服务」这两种解释。
目标机事件日志
在目标虚拟机上用 Get-WinEvent 取出 Application Error 记录:
1 | Get-WinEvent -FilterHashtable @{LogName='Application';ProviderName='Application Error'} -MaxEvents 20 | |
输出是:
1 | 2026/10/16 17:45:03 异常代码: 0xc0000374 |
第一条的时间与失联时刻完全对齐。前面几项都是我们在外面观察到的现象,这一项是目标机自己记下的 STATUS_HEAP_CORRUPTION,证据强度最高。
主机层对照
1 | 目标地址 ARP 状态:Reachable(反复) |
目标机 ARP 保持 Reachable,而飞秋不再应答,说明是应用进程死亡,不是断网或断电。
成功率与测量口径
单个报文一次打崩并不可靠。按远程等效条件测量,单个报文的成功率是 3/5,平均耗时 7.0 秒;连续施加多个不同分配尺寸的分片写之后,成功率是 5/5,平均 2.1 秒。之所以要打多个落点,是因为一次崩溃取决于越界写的落点是否踩中正在使用的堆结构,不同分配尺寸会让分配落到不同的堆大小类,从而覆盖不同的堆状态。
测量口径需要说明。远程读不到目标进程的状态,只能靠 UDP 探测有没有回包来判断存活,而探测报文本身会为目标建立对端记录,改变目标的堆状态。这里的成功率都是在这个条件下测的。
把附加数据缩到 64 字节、整包 177 字节的「极限最小包」成功率只有 2/5,只适合做最小打击面演示。
影响
| 维度 | 评估 |
|---|---|
| 攻击成本 | 极低,4 个 UDP 报文,无认证、无交互、无前置条件 |
| 影响范围 | 与目标同网段任意主机均可发起;飞秋在局域网内使用广泛且默认监听 |
| 服务影响 | 即时通讯完全中断,消息、文件传输、插件服务一并失效 |
| 恢复成本 | 必须人工重启;无自愈机制 |
配合持续压制时,服务恢复后 8 秒内可以再次打掉,管理员无法维持飞秋可用。
修复建议
核心是在 memcpy 之前把两个数卡住。
1 | if (offset >= 0 && (unsigned)offset + Size <= dwBytes) { |
三个要点。
offset 必须按无符号比较。它是 _wtoi 出来的有符号数,如果直接写成 offset + Size <= dwBytes,负的 offset 会绕过检查。
dwBytes 要给上限。当前它由 wcstoul 得来且没有上界,超大值会导致 GlobalAlloc 失败并提前返回,虽然不崩,但属于异常输入。
分片序号与位图索引要对齐校验。现在 seq 只校验了 1 到 8000,而位图写入位置是 entry + 0x2F + seq,应确认该位置确实落在位图区间内。
其他建议:网络数据接收路径启用 /GS、CFG、SafeSEH;为目标进程配置异常重启守护,缩短中断窗口。飞秋已长期停止维护,建议整体迁移到仍在维护的即时通讯方案。
工具
审计产出的工具在下面的仓库里,最终交付物是一个单文件 Win32 程序,12 KB,零 CRT 依赖,兼容 Windows XP SP3 及以上,提供单次、循环压制、定时三种模式和命令行接口。
https://github.com/Moxin1044/feiq-dos-tool
结论
成因是三个来自网络的内存操作参数只校验了一个。它不需要攻击技巧就能稳定触发,在局域网环境下具备完整的拒绝服务能力。
审计里花时间较多的是把偶发崩溃做成稳定可用:单个报文打不稳,需要覆盖不同的堆落点,才能把成功率提上去。整个过程靠「构造报文、观察进程行为」的闭环推进。
本文列出的数字与现象都可以复现。文中测试在受控实验网络内完成,目标是自己搭建的同版本样本,未涉及任何第三方资产。








