群晖 C3RB3R 勒索事件:漏洞技术分析

群晖 C3RB3R 勒索事件:漏洞技术分析

整理时间:2026-11-10
资料来源:Innora Security Research(CVE-2026-13639 反汇编分析)、Rapid7 DEF CON 33 议题、Synology-SA-26:13/26:14 官方公告、CosmicBytez Labs、360 反勒索团队归因
分析对象:DS420plus,DSM 90080


一、先把因果理清

这轮事件的链条不是一条,而是三条并行:

链一(主入口)  CVE-2026-13639  会话密钥全零 → 伪造 JWT → 免密登录  
链二(同批)    CVE-2026-13684  SCGI 输出编码 → 任意文件读写  
链三(老洞)    CVE-2024-10441  两年前修的洞,没升级的机器还在中  
                ↓  
           拿到 admin / root  
                ↓  
         植入 /etc/cron.d/synot* 定时任务  
                ↓  
          下载 C3RB3R 加密程序  
                ↓  
          文件改名 .L0CK3D + read-me3.txt  

三条链里,链一最致命,因为它绕过了你认为"最有用"的所有防护。


二、CVE-2026-13639:会话密钥变成 32 个零

2.1 一句话

DSM 生成会话 JWT 签名密钥时,把刚清零的栈缓冲区写进了文件,而不是随机数生成器的输出。结果密钥是 32 个 0x00。

2.2 反汇编证据

libsyno-pam-login.so.7.0 里的 synojwt::GetOrCreateSessionHashKey():

movaps [rsp], xmm0          ; 栈缓冲清零(32 字节 = 0)  
movaps [rsp+0x10], xmm0  
...  
lea rdi, [0xfbf00]          ; randombytes_buf(全局缓存, 32)   ← 随机数写进全局缓冲  
call randombytes_buf  
mov rdi, rsp                ; fwrite(栈缓冲, 1, 32, f)         ← 却从栈读!  
call fwrite                 ; → hash.key 写入 32 个零字节  
...  
lea rdi, [0xfbf00]          ; fread(全局缓存, 1, 32, f)        ← 再读回来  
call fread                  ; → 内存中的密钥 = 32 × 0x00  

这里有两层错:

  1. 随机数生成器调用正常,输出落在全局缓冲 0xfbf00
  2. 但 fwrite 用的是 rsp(栈),那个缓冲区刚被 movaps 清零

于是磁盘上的密钥文件是 32 个零字节,内存里读回来的也是 32 个零字节。

这不是"熵不足",是熵为零——密钥是个常量。

2.3 修复对比

项目 漏洞版(72806-8 及以前) 修复版(72806-9)
写入源 rsp(清零的栈) r12(随机数缓冲)
打开方式 fopen("w") open64(O_WRONLY|O_CREAT|O_TRUNC|O_EXCL|O_NOFOLLOW, 0600)
权限 随 umask fchmod(0400) + fchown
目录 /usr/syno/etc/private/session/ 权限 1777 收紧

注意那个 1777 的目录权限——它意味着任何本地用户都能在这个目录里预置文件,配合 fopen("w") 不检查符号链接,还有一个本地提权面。

2.4 利用链:为什么密码和 2FA 都失效

① 伪造 JWT  
   header  {"alg":"HS256","typ":"JWT"}  
   payload {"sub":"admin","aud":"2FA","iat":<now-60>,"exp":<now+3600>}  
   signature = HMAC-SHA256(signing_input, 32×0x00)   ← 密钥已知  
  
② POST /webapi/entry.cgi  api=SYNO.API.Auth method=login  
   account=admin   passwd=<伪造的 JWT>              ← 密码框填 JWT  
  
③ DSM 的 PAM 栈 /etc/pam.d/webui 执行顺序:  
  
   auth [success=3 ...] pam_syno_1fa_tmptok.so      ← 第一步  
        SynoPAMVerifyTmpToken(user, "2FA", token)  
        · 检查 sub == 用户名          ✓  
        · 检查 aud == "2FA"           ✓  
        · 检查 iat ≤ now ≤ exp        ✓  
        · 用 hash.key 验 HS256 签名   ✓(密钥全零,攻击者也有)  
        → 验证通过,success=3 直接跳过后面 3 个模块  
  
   (被跳过的)  
   auth ... pam_unix.so            ← 密码校验,跳过  
   auth ... pam_syno_winbind.so    ← 域认证,跳过  
   auth ... pam_syno_ldap.so       ← LDAP,跳过  
  
④ 继续走 pam_syno_otp.so  
   · 对"未启用 2FA 的账户"直接通过  
   · 对启用了 2FA 的账户——但前面已经 success=3 跳过了  
  
⑤ 结果:无密码、无 2FA,认证为该用户(可以是 admin)  

关键点:pam_syno_1fa_tmptok.so 在密码模块之前。这是设计如此——"一次性临时令牌"本来就应该跳过密码。问题在于验证这个令牌的密钥是可预测的。

2.5 这解释了三件"反常"

你以为的保护 为什么没用
16 位复杂密码 密码模块根本没被调用
手机令牌 2FA 1FA 临时令牌被当作"已通过 2FA",且 success=3 跳过
错 5 次封 IP 攻击者一次就成功,没有"错误"可计
改默认端口 只改了路径,不改变漏洞

你所有的防护都建立在"攻击者需要猜密码"这个假设上。而这个漏洞让攻击者根本不需要碰密码环节。


三、CVE-2026-13684:SCGI 输出编码

比链一简单,但同样是 9.8。

SCGI 的定位:DSM 前端 Web 服务器 ⇄ 后端服务进程之间的协议  
                (作用类似 FastCGI)  
  
缺陷:输出越过这个接口时,没有正确编码/转义  
     → 攻击者可以构造畸形 SCGI 请求  
     → 读写预期范围之外的文件  
     → 或者让后端服务崩溃(DoS)  

没有公开的反汇编级细节,Synology 只说"improper encoding or escaping of output"(CWE-838)。

危害同链一:未认证的任意文件读写。拿到这个,攻击者可以直接写 /etc/cron.d/ 或者 SSH 公钥。


四、CVE-2024-10441:两年前的老洞

这是最值得说的一条。

CVE-2024-10441    2024 年 12 月就修复了  
                  2026 年 10 月还在被大规模利用  

为什么:

  1. 群晖的更新是分阶段推送——你的控制面板可能显示"已是最新",但实际版本号没到
  2. 很多用户早年更新踩过坑(升级后套件不兼容),于是关掉了自动更新
  3. NAS 是"装完就忘"的设备,放在角落几年不动

结果就是:一个已经修复两年的漏洞,仍然有大量设备在被打。

这轮的受害者里,有一部分就停在 DSM 7.2.1 Update 5 这类老版本上,连 2024 年那个洞都没修。


五、C3RB3R 勒索样本做了什么

5.1 五个动作

① 查杀安全进程     遍历并 kill 常见的杀毒/监控进程  
② 识别 CPU 架构    自动判断 x86_64 还是 ARM → 拉对应编译版  
③ 建防重标记       /bin/l0ck.enc  
④ 拉双备份主程序   /bin/qnetd(主)+ /bin/libgld(备)  
⑤ 自删痕迹         加密完删掉 /bin/qnetd、/etc/cron.d/*、/bin/l0ck.enc  

5.2 为什么它"不搞死"系统

它硬编码跳过了 27 个群晖核心系统目录:

/boot  /etc  /usr/syno  /var/packages  ...  

目的不是仁慈,是商业——系统必须能开机,受害者才能看到 read-me3.txt 并去付钱。

5.3 分级加密策略

> 5MB 的普通文件   每隔 5MB 只加密头部 1MB  
                  → 几秒钟废掉一部几十 GB 的蓝光原盘  
                    
187 个高价值扩展名  全文件加密  
                  (数据库、财务账套、虚拟机镜像……)  

这是针对 NAS 场景优化的——NAS 里动辄几万部电影、几十 TB 数据,全量加密跑不完,用户会中途拔电源。

5.4 加密算法

每个文件:ChaCha8 独立 256 位密钥 + 64 位 nonce  
再用内置 RSA-2048 公钥加密文件密钥  

没有私钥,数学上无法暴力破解。 这就是为什么"别付赎金"之外,唯一的出路是备份。


六、对你的机器:一个必须立刻检查的点

你升级到了 DSM 90080,超过了修复基线 7.4-90075。

但是——Innora 的分析里有这么一句:

升级后若密钥全零:删除 /usr/syno/etc/private/session/hash.key(修复版代码会在下次登录正确重建)——零密钥签名的会话应视为已泄露。

为什么"升级了还可能有问题":

修复版代码的逻辑是 GetOrCreateSessionHashKey —— 名字里的 "OrCreate"  
意思是:文件存在就直接用,不存在才创建  
  
你是从旧版本升级上来的  
→ hash.key 在升级前就已经存在了(内容是 32 个零)  
→ 升级后,代码看到文件存在,直接读它  
→ 密钥仍然是 32 个零  

所以升级不一定解决了这个问题。 必须实际看一眼。

检查命令(需要 SSH)

xxd /usr/syno/etc/private/session/hash.key  

判读:

00000000: 0000 0000 0000 0000 0000 0000 0000 0000   ← 全零 = 仍有问题  
00000000: 0000 0000 0000 0000 0000 0000 0000 0000  
→ 必须删除这个文件  
  
00000000: a3f2 91c8 4e7b ...                          ← 随机字节 = 正常  
→ 无需处理  

如果确认是全零

# 1. 删除(修复版会在下次登录时用正确逻辑重建)  
rm -f /usr/syno/etc/private/session/hash.key  
  
# 2. 强制所有会话失效(重启相关服务)  
synosystemctl restart nginx 2>/dev/null || /usr/syno/bin/synosystemctl restart nginx  
  
# 3. 重新登录 DSM(这时会生成新密钥)  

副作用:所有已登录的会话会失效,需要重新登录。这是好事——那些会话可能已经被伪造过。

顺便查入侵痕迹

# 恶意文件  
ls -la /bin/qnetd /bin/libgld /bin/l0ck.enc 2>/dev/null  
  
# 恶意定时任务  
ls -la /etc/cron.d/ | grep -i synot  
  
# 被加密的文件  
find /volume1 -maxdepth 3 -name "*.L0CK3D" -o -name "README*.txt" 2>/dev/null | head  

七、为什么这次事件值得记住

7.1 安全假设被打破了

普通人装 NAS 的安全思路是:

改端口 + 强密码 + 2FA + 关闭 admin 账号 = 安全  

这四件事全部建立在"认证环节是可信的"这个前提上。

CVE-2026-13639 直接把认证环节本身变成了一条绕过通道——它不是"猜密码猜得更快",是"根本不去猜"。

推论:任何"仅靠认证强度"的防护,在认证逻辑本身有缺陷时全部归零。

7.2 "最新版"不等于"已修复"

三个层次:

① 控制面板说"已是最新",但分阶段推送没到你 → 版本号要自己看  
② 你打了 DSM 补丁,但套件(PHP/Web Station)没打 → PHP2Shell 还在  
③ 你升级了,但升级前的坏状态被继承(hash.key 全零)→ 补丁不生效  

7.3 备份的形态决定了能不能救命

备份方式 勒索时
RAID 无效,还把乱码同步到每块盘
实时双向同步(Cloud Sync) 有害,把加密文件推上云覆盖好的
常年插着的 USB 硬盘 无效,root 权限一锅端
Btrfs 不可变快照 有效,C3RB3R 没清快照
离线拔掉的硬盘 / 只读异地 有效

文章里那句话是对的:

你的"实时备份"在替病毒打工。


八、给你的行动清单

✅ 已完成  
   DSM 升级到 90080(超过基線 7.4-90075)  
   全部共享文件夹启用不可变快照  
   公网零暴露(22 端口全关,域名零命中)  
   SSH 关闭  
  
⚠️ 待确认(SSH 关闭,需要你开一下)  
   ① xxd /usr/syno/etc/private/session/hash.key  
      全零 → 删除,重新登录  
   ② 查 /bin/qnetd、/etc/cron.d/synot*、*.L0CK3D  
  
📋 建议补  
   ③ Hyper Backup 重装(或接受只有快照)  
   ④ NFS 收紧授权范围(现在是 192.168.31.0/24 + no_root_squash)  
   ⑤ PHP 老版本停用(7.3/7.4/8.0/8.1 全异常,留 8.2 即可)  

最优先的是 ①。这是唯一一个"升级了但可能还没修好"的点。


附:时间线

2024-12        CVE-2024-10441 修复(后续仍被大量利用)  
2025-08        Rapid7 在 DEF CON 33 讲 DisguiseDelimit(分隔符注入)  
2026-09-18     Synology-SA-26:13 发布,修复 8 个 DSM 漏洞  
               CVE-2026-13639 / 13684 / 13635  
2026-10-21     PHP 官方紧急修复 PHP2Shell(CVE-2026-109618/109619)  
2026-10-22     Synology-SA-26:14,PHP 套件安全更新  
2026-10-28     凌晨,全球范围 C3RB3R 集中爆发  
2026-10-29     Synology 官方声明  
2026-11-02     国内高校发布排查通知  
2026-11-10     本文整理