Sandworm 使用 Cyclops Blink 替代 VPNFilter
· 4 分钟阅读 · @admin

Sandworm 归属于俄罗斯总参谋部主要情报局主要特殊技术中心军事单位 74455。Cyclops Blink 是该组织针对网络设备开发的恶意程序,用来替代 2018 年曝光的 VPNFilter。
1 概述
Cyclops Blink 是一个恶意的 Linux ELF 可执行文件,使用 PowerPC 大端或 x86 架构编译。它由一个核心组件和多个模块组成。模块通过 fork 作为子进程执行,模块之间用 pipe 通信。
按 CISA 的描述,Cyclops Blink 像是 VPNFilter 的替代框架。它利用网络设备,主要是小型办公和家庭办公(SOHO)路由器以及 NAS。当时看到的部署主要用于监视设备,但 Sandworm 也很可能能把同一套框架编译到其他架构和固件上。
样本带四个内置模块,启动时就会执行,分别负责文件上传和下载、收集系统信息、更新恶意软件。C&C 还可以下发指令再添加模块,恶意软件用 execlp 执行这些模块。
和 C&C 通信时使用 TLS。RSA 公钥、RSA 私钥和 x.509 证书硬编码在程序数据段里。C&C 地址列表和端口列表也是硬编码的,不同样本里的内容不一样。
2 流程图

3 样本
| 文件名 | MD5 |
|---|---|
cpd |
1d7b16b333058a584f5152cfc965a2a6 |
install_upgrade |
4415822cfcd2dbb3e5a3e9ade0636bcb |
4 详细分析
4.1 核心模块
启动时先检查自己是不是以 [kworker:0/1] 作为进程名运行。带方括号的进程名通常是内核线程在用。如果不是,就 fork 出一个子进程,在子进程里用 execl 重新加载自身,然后父进程退出。主要恶意代码在这个子进程里执行。
接着修改 Linux iptables,放行指定端口的 TCP 流量。这些端口用来和 C&C 通信。然后启动四个内置模块。
核心模块从正在运行的模块接收消息,发给 C&C,再解密并解析 C&C 返回的数据。
4.2 文件上传下载模块
这个模块负责上传和下载,模块 ID 是 0x0F。收到的命令格式如下:
struct format {
uint8_t control_flags;
uint8_t url_len;
uint8_t path_len;
char *url;
char *path;
};
每收到一条命令就创建一个子进程来处理,所以可以同时处理多条命令。
control_flags 控制模块行为:
| 标志 | 作用 |
|---|---|
0x01 |
把下载的文件作为子进程执行 |
0x02 |
把下载的文件作为新模块加入 Cyclops Blink |
0x04 |
置位则把内容当 shellcode 在内存中执行,否则下载到指定路径 |
0x08 |
置位则从 C&C 下载,否则从指定 URL 下载 |
0x10 |
置位表示上传,url 指向要上传的文件;否则是下载 |
0x20 |
下载已完成 |
0x40 |
从相对路径下载 |
0x80 |
从绝对路径下载 |
处理指令的代码:

下载时按 control_flags 走不同分支:

如果请求的下载链接返回数据里带 :location 重定向关键字,就根据链接首字符判断重定向 URL,并据此设置 control_flags。

4.3 系统信息收集模块
这个模块从 WatchGuard 设备收集系统信息,模块 ID 是 0x08。它定时执行 Linux 命令、读取包含系统信息的文件,再把结果发给核心模块,由核心组件上传到 C&C。
| 命令或路径 | 用途 |
|---|---|
uname |
内核名称和信息 |
sysinfo |
内存信息 |
statvfs |
当前工作目录的文件系统信息 |
if_nameindex |
网络接口名称 |
/etc/issue |
发行版 |
/etc/passwd |
用户信息 |
/etc/group |
用户组 |
/proc/mounts |
挂载信息 |
/proc/partitions |
分区信息 |
/proc/net/arp |
ARP 表 |
收集系统信息的代码:

4.4 更新 C&C 地址列表
这个模块负责维护 C&C 地址列表。设备启动时从 rootfs_cfg 读取地址列表,再把数据发给核心组件。

4.5 持久化模块
模块 ID 是 0x51,负责更新恶意程序并维持驻留。启动时会处理 /etc/wg/configd-hash.xml、/etc/wg/config.xml 等 WatchGuard 配置文件。
它响应核心模块的命令:
| 命令 | 作用 |
|---|---|
0x00 |
以只读权限重新挂载根文件系统 |
0x01 |
以读写权限重新挂载根文件系统 |
0x02 |
更新 Cyclops Blink 可执行文件 |

收到 0x02 后进入更新流程:

如果存在 /pending/WGUpgrade-dl,先做三件事:
- 把
/bin/install_upgrade复制到/pending/bin/install_upgrade。 - 把
/usr/bin/cpd复制到/pending/bin/cpd。 - 把
/etc/runlevel/4/s51armled复制到/pending/bin/S51armled。

然后用嵌在文件里的 ELF 覆盖合法的 install_upgrade,再用改过的程序去安装升级后的设备固件。

WatchGuard Firebox 用硬编码密钥计算哈希,因此可以重算固件的 HMAC,绕过设备对固件的校验。

随后用 utime 改这些文件的访问和修改时间,让它们和 /pending/sysa_code_dir/bin/busybox-rel 的时间戳一致。

重打包和解压通过 /bin/sh 调用 gunzip 和 gzip。

打包完成后删掉解压时创建的文件,清掉操作痕迹。

最后用事先复制到 /pending/bin/install_upgraded 的合法 /bin/install_upgrade 完成安装,并重启 WatchGuard 设备。RC 脚本 S51armled 保证设备重启后再把 Cyclops Blink 拉起来。

4.6 命令与执行
每次 beacon 都会从当前 C&C 列表和硬编码端口列表里随机选一个目标。
每条 beacon 单独用 AES-256-CBC 加密。OpenSSL_EVP_SealInit 为每条消息随机生成密钥和 IV,再用硬编码的 RSA 公钥加密这把密钥。收到的 beacon 用 OpenSSL_RSA_public_decrypt 和解密用的硬编码 RSA 公钥处理。解密后的格式是:
struct format {
uint16_t cmd_len;
uint16_t target_mod_id;
uint32_t cmd_id;
char *cmd_data;
};
| 指令 | 作用 |
|---|---|
0x00 |
结束运行 Cyclops Blink 的进程 |
0x01 |
强制发送 beacon |
0x02 |
更新 C&C 的 IPv4 地址列表 |
0x03 |
设置下一次 beacon 的时间 |
0x04 |
设置 beacon 间隔 |
0x05 |
往 Cyclops Blink 添加一个新模块 |
0x06 |
重启 Cyclops Blink |
0x07 |
设置一个未知数值 |
0x08–0x0A |
把配置重新发给正在运行的模块 |
0x0B |
把硬编码的 RSA 公钥发给指定模块 |
0x0C |
把硬编码的 RSA 私钥发给指定模块 |
0x0D |
把硬编码的 x.509 证书发给指定模块 |
4.6.1 Beacon 时间
0x01、0x03 和 0x04 都是在改模块发送 beacon 的时间。

4.6.2 重启
fork 出一个子进程后退出当前进程,进程名设为 [worker/0:1]。

4.6.3 把密钥发给模块
命令 0x0B 到 0x0D 时,核心模块把 TLS 用的密钥发给指定模块。模块 ID 由缓冲区偏移 6 处的值决定。

密钥类型由命令 ID 决定。发给模块时,会在密钥前面加上 s: 和左尖括号这类前缀,让模块能认出密钥类型。

这些密钥硬编码在文件里。

5 IOC
哈希:
1d7b16b333058a584f5152cfc965a2a64415822cfcd2dbb3e5a3e9ade0636bcb
路径:
/pending/bin/install_upgraded/var/tmp/a.tmp/usr/bin/cpd
更多 cpd 样本,原稿记录的检测结果都是 TopEDR 未报、YARA 规则 Topsec-APT.Sandworm.CyclopsBlink.a 命中:
| MD5 | YARA |
|---|---|
9b33ba6c689b63b24424f28fb629ab11 |
Topsec-APT.Sandworm.CyclopsBlink.a |
d72901696ade1128e8ef42cf6a81e20d |
Topsec-APT.Sandworm.CyclopsBlink.a |
d6a193f48b5acc02010962fdc737e4c3 |
Topsec-APT.Sandworm.CyclopsBlink.a |
a4d01af3247a81607cf0ba898940b559 |
Topsec-APT.Sandworm.CyclopsBlink.a |
aa35d45d0c7ad31d1bd5c54d31fd06ab |
Topsec-APT.Sandworm.CyclopsBlink.a |
12053b6e329902d3a9723a88f3b99278 |
Topsec-APT.Sandworm.CyclopsBlink.a |
1d7b16b333058a584f5152cfc965a2a6 |
Topsec-APT.Sandworm.CyclopsBlink.a |
af22b6f54f03160ab2fbc8b5d92f8938 |
Topsec-APT.Sandworm.CyclopsBlink.a |
6 ATT&CK
| 战术 | ID | 名称 | 在这个样本里 |
|---|---|---|---|
| Execution | T1059.004 | Unix Shell | 执行 shell 命令 |
| Persistence | T1037.004 | RC Scripts | 被修改的 S51armled 在启动时拉起恶意程序 |
| Persistence | T1542.001 | System Firmware | 通过被修改的固件驻留 |
| Defense Evasion | T1036.005 | Match Legitimate Name or Location | 进程名伪装成系统线程名 |
| Discovery | T1082 | System Information Discovery | 用函数或读文件收集系统信息 |
| Command and Control | T1132.002 | Non-Standard Encoding | C&C 消息使用自定义格式 |
| Command and Control | T1008 | Fallback Channels | 多个备用 C&C 地址和端口 |
| Command and Control | T1573.002 | Asymmetric Cryptography | AES-256-CBC,每条消息随机密钥和 IV,再用硬编码 RSA 公钥封装 |

