最新消息:本站原qzkyl域名已转让,后期该域名所有言论与本站无关,同时本站已改名ipve虚拟机,交换友链请发送邮件zc#ipve.cn(#换@)

无密钥也能解密C3RB3R勒索病毒:用 Btrfs 旧数据块找回群晖 NAS 的 229GB 数据

NAS相关 ipve 11浏览 0评论

2026 年 10 月下旬,C3RB3R 勒索病毒席卷了一批群晖 NAS:照片、文档、备份文件全部被加上 .L0CK3D 后缀,每个文件夹里放着一封指向 Tor 地址的 read-me3.txt 勒索信。这轮攻击的原理和防御方案,我们上一篇《群晖NAS遭遇C3RB3R勒索病毒:攻击原理与完整防御指南》已经讲过,这里不重复。

对中招的人,最折磨的问题是:没有快照、没有备份、又不想付赎金,数据是不是真的没救了?教科书式的答案一直是”没救”——它用 ChaCha8 加密数据、RSA-2048 包裹密钥,理论上无法暴力破解。

但最近一个真实的恢复案例证明了还有第三条路:不需要密钥。利用 Btrfs 文件系统写时复制(Copy-on-Write)的特性,这位受害者从盘上精确找回了 35,445 个文件、229.9GB 数据,并且抽检验证了约 20 万个 4K 块,全部与 Btrfs 自己保存的校验和一致。

这篇文章把这套”无密钥恢复”的思路完整讲清楚:为什么原理上成立、需要什么条件、具体怎么操作、以及哪些数据最后确实救不回来。

C3RB3R勒索病毒加密群晖NAS文件主题图
C3RB3R 勒索软件:没有密钥,也可能拿回你的数据

一、C3RB3R 是怎么加密的?关键在”只加密一部分”

先看两个决定一切的事实。第一,C3RB3R 得手后的”标准动作”:

  • 加密:用 ChaCha8 算法加密用户数据,密钥再用 RSA-2048 包裹,理论上无法暴力破解;
  • 改名:被加密的文件追加 .L0CK3D 后缀;
  • 留勒索信:在目录里投放 read-me3.txt,指向一个 Tor(.onion)地址,要求联系”后台”支付赎金。

下图是 360 与群晖确认的攻击链,以及可以阻断攻击的环节:

C3RB3R攻击链示意图
C3RB3R 攻击链:从未修补的 DSM 到批量加密(来源:Synology / 奇虎 360)

第二,也是”无密钥恢复”成立的关键——对密文文件的逆向分析发现,C3RB3R 并不加密整个文件,而且是在文件原址上改写。它的加密模式按文件大小分档:

  • 约 1MiB 以下的文件:整文件加密(模式 0x24);
  • 1–5MB 的文件:只加密前 1MiB(模式 0x26);
  • 更大的文件:加密头、中、尾三段,加起来约占文件的 12%(模式 0x25);
  • 数据库文件(.db、.db-wal 等):不论大小,一律整文件加密;
  • 加密完成后在文件末尾追加 588 字节的尾部(含加密的密钥材料、会话标记、原始文件长度),然后整体改名 *.L0CK3D。

换句话说,一个 2GB 的视频,真正写成密文的只有约 250MB,其余 95% 还是原始明文。这种”原地局部改写”的行为,是所有恢复操作的出发点。

下图是 DiskGenius 中截获的真实勒索信,可以看到 “C3RB3R INSTRUCTIONS” 和 Tor 解密链接:

C3RB3R真实勒索信read-me3.txt内容
真实勒索信 read-me3.txt:指向 Tor 地址的解密说明

二、明文为什么还在盘上?Btrfs 的写时复制

原地改写,为什么没有把原始数据直接覆盖掉?因为群晖的数据卷用的是 Btrfs 文件系统,它有一个核心机制:写时复制(CoW)——

修改 CoW 文件里的数据时,Btrfs 会把新数据写到新位置,再把文件的映射指向新位置,旧数据块不会被擦除。

关键细节在于:Btrfs 不拆分 extent(数据块)。比如一个 5.5MB 的视频,原本是一整块 extent 存着的;C3RB3R 只改写了头、中、尾三段。改写之后,文件里未加密的部分仍然引用着那块原始 extent,只是带了不同的偏移量。只要还有任何引用存在,整块原始 extent 就保持已分配状态——包括被改写那三段对应的原始明文。

这种结构被称为 “bookend extent”(首尾块)——好比一本书的封面和封底钉死了,中间的书页就抽不走。未加密的首尾两段,”拽着”旧数据块不放,所以被加密段落对应的明文还留在盘上。

由此可以直接推出恢复规律:

  • 部分加密的文件(大于约 1.004MB 的照片、视频,占绝大多数):可以完整找回;
  • 整文件加密的小文件(1MiB 以下):所有引用都换成了新 extent,旧 extent 被释放、可能已被复用——救不回;
  • 加密段长于 128MiB(单个 extent 的长度上限):中间可能有整块原 extent 被完全覆盖后释放,只能推测它原来的位置——不保证。

NoCOW 共享也不是死局:last_snapshot 例外

DSM 里创建共享文件夹时如果没勾”数据校验和”,这个共享就是 NoCOW(直接原地覆盖),按上面的逻辑似乎没救。但 Btrfs 有一个例外规则:

只要一个 extent 的代数(generation)不大于所属子卷的 last_snapshot,覆盖写就必须走 CoW。因为这块数据可能还被某个快照共享着,内核不能原地改它。

两个关键点:

  • 即使快照后来被删除,last_snapshot 的值也不会清零;
  • 群晖的每个共享文件夹都是一个子卷。只要它曾经做过快照(Snapshot Replication、Hyper Backup 的某些模式、手动快照都算),快照之前写入的数据在 NoCOW 共享里同样可以恢复。

真实案例中,document 和 movie 共享的 lastsnap 对应 2024 年 6 月前后的一次快照:那之前存入的约 1 万个文件得以精确还原,之后写入的约 28.7 万个文件则已被原地覆盖。

三、无密钥恢复的四步操作路线

第 1 步:先确认加密进程是否还活着(收益最大的窗口)

如果事发不久(几小时内),加密进程可能还在运行(公开案例里它以 /usr/bin/libgld -n 308 的形式存在,程序文件已从磁盘删除)。在 NAS 上检查:

ls -l /proc/*/exe 2>/dev/null | grep -i deleted
ps -eo pid,etime,args | grep -v '\[' | sort -k2 | tail -50

发现之后,立即用 kill -STOP <pid> 冻结它——不要 kill,也不要重启机器——然后按公开救援项目从进程内存里取出会话主密钥。这是唯一能把文件全部解密的路线,收益远大于后文的 Btrfs 路线:

如果进程已经退出(重启过,或事发已十几小时),内存里的密钥基本找不到了。第一个案例里,对 16.7GB 物理内存、9.8GB zram、2GB 磁盘 swap 做了逐字节穷举扫描,又用内存中找到的 194 个 ChaCha 状态对 77.8 万个加密文件逐一试解,结果是零命中。这时候就别在内存上反复投入,直接走 Btrfs 路线。下图是基于群晖官方清单和 CISA 指南整理的应急响应顺序:

C3RB3R应急响应顺序图
被 C3RB3R 命中后的响应顺序(来源:Synology 清单 + CISA 指南)

第 2 步:安全红线(决定成败)

  • 不要删除、移动或覆盖任何 .L0CK3D 文件,不要清空回收站——旧数据块正是靠密文文件”拽着”才没被释放,删掉密文等于销毁原件;
  • 不要做 defrag、balance、btrfs check --repair,不要运行来源不明的”解密器”——defrag 会重写 extent,直接毁掉证据;
  • 尽量减少卷上的写入:暂停 NVR 录像、下载任务、Photos 索引、同步任务;
  • 恢复结果写到 NAS 以外的盘,绝不回写原目录;
  • NAS 端的所有操作保持只读:只做查询、读文件、读块设备。

⚠️ 注意:重启 NAS 或升级 DSM 本身不会破坏被引用的旧 extent,但会丢掉内存现场、并带来大量写入——等数据导出完成再做。

第 3 步:把旧 extent 映射出来,逐字节取回原始数据

前面两步做完,实际恢复可以概括为一句话:把 C3RB3R 改写过的每一个字节,通过 extent 树映射回旧数据块,从块设备只读地读回来。技术核心有三处:

1. EXTENT_DATA 树的字节级映射。通过 BTRFS_IOC_TREE_SEARCH ioctl 按 inode 查询文件的每一段映射:起点、写入代数、extent 起始逻辑地址、extent 总长、段内偏移、段长。由此可以精确推出旧 extent 覆盖的文件区间,任何一个文件位置的原始字节对应哪个逻辑地址,算出来即可,不需要猜。(早期方案只靠 FIEMAP 拿相邻区段差值推算,看不到 extent 的完整长度,在约 50 个大视频上读错了数据;换成 extent 树之后,哪些字节能精确取回、哪些只能推测,一目了然。)

2. G_ATTACK 区分新旧。加密时新写的 extent,代数集中在攻击时间窗口内。取被加密文件 pos=0 段的代数最小值,减去一点余量就是 G:代数不小于 G 的 extent 是密文,小于 G 的是旧数据。案例里攻击期新写入的代数是 4,842,057–4,842,378,而攻击前最近一次正常写入停在 4,668,578——中间一大段空档,正是判断正确的证据。G 定错会直接把密文当旧数据读回来,所以工具内置了边界自校验来兜底。

3. 校验和树逐块证实。开启数据校验和的共享(即 CoW 共享),Btrfs 的校验和树里存着每个 4K 数据块的 CRC32C。旧 extent 只要还被引用,它的校验和记录就还在。把读回的每个 4K 块算一遍 CRC32C 与记录比对——一致,就说明读回的正是加密前的数据,这是恢复成功最强的证据。

第 4 步:四层验证,分开登记

取回的数据要经过四层验证,才配得上”救回来了”三个字:

  1. SHA-256:本地解包结果与 NAS 端 manifest 一致,排除传输错误;
  2. 校验和证实:逐 4K 块比对 CRC32C(仅 CoW 共享可用);
  3. 完整解码:图片用 Pillow/pillow-heif 完整解码,音视频用 ffmpeg 解码;
  4. 已知原件对照:拿手机、云盘、幸存压缩包里的副本逐字节比对。

案例里有两个值得记住的判读:校验和全部一致、但解码报错的文件,说明是原文件本身就坏(老 DVDRip、录屏、iPhone HEVC 很常见),应算恢复成功;校验和一致、却与”已知原件”对不上时,先怀疑本地副本——案例里就有一份本地副本恰好坏了一个 4K 块,反而是 NAS 侧恢复的结果是对的。

四、真实案例战绩:救回了什么,救不回什么

第一个案例的完整数据(群晖 DSM 7.2.1,SHR 卷):

  • CoW 共享(homes、photo 等):找回 35,445 个文件 / 229.9GB,全部通过 SHA 核对与完整解码验证;
  • 校验和抽检:约 20 万个 4K 块,与 Btrfs CRC32C 全部一致;
  • NoCOW 共享:凭快照例外又找回约 6,100 个文件 / 346GB(另有约 3,800 个文件在本地已有原件)。

如实告知:救不回来的部分——

  • 1MiB 以下整文件加密的小文件、数据库文件:旧 extent 已释放并被复用;
  • NoCOW 共享中晚于最后一次快照写入的文件:已被原地覆盖。不过大文件其余 88% 还在原处,视频可能部分可播,可以试 untrunc 用同一设备拍的完好视频做参照重建索引;
  • 大文件跨 extent 的推测恢复:案例卷的数据区写满 99%,88 个推测类压缩包只有 4 个完整通过 CRC 校验,约一半只坏了 1 个成员——被释放的旧数据块大多已被新数据占用。卷的空闲空间越大、事发后写入越少,推测成功率越高。

只坏一个成员的压缩包也不全废:用解压工具”跳过错误继续解压”,其余成员照样能救出来。

群晖NAS
数据卷的每一次写入,都在为恢复留下可能性

五、工具怎么用:c3rb3r-btrfs-recovery

案例作者把整套流程做成了开源项目 crt106/c3rb3r-btrfs-recovery(MIT 协议),两种用法:

  • AI Agent Skill:给 Claude Code、Codex、Cursor 等支持 SKILL.md 的 agent 用。装好之后直接描述你的情况(”我的群晖昨晚被 C3RB3R 勒索了,文件都变成 .L0CK3D,没有快照也没有备份”),agent 会按流程自动执行:判断内存密钥窗口 → 环境自检 → 确定 G_ATTACK → 单文件验证 → 全量普查 → 流式导出 → 多重验证 → 出报告;
  • 手动脚本:不借助 agent 也能跑,NAS 端只需要 Python 3.6+ 标准库(DSM 7 自带 3.8),全程只读。
# 安装为 skill
npx skills add crt106/c3rb3r-btrfs-recovery

# 手动:先在任意 .L0CK3D 文件上跑自检
cd skills/c3rb3r-btrfs-recovery/scripts
python mkjob.py -- preflight /volume1/某文件.jpg.L0CK3D > job.sh
{ printf '%s\n' "$SUDO_PW"; cat job.sh; } | ssh admin@NAS "sudo -S -p '' sh -s"

手动流程建议顺序:

  1. preflight:环境自检(Python 版本、root、是否 Btrfs、超级块、dump-tree、端到端读取比对、与 btrfs-map-logical 交叉验证……),逐项给 OK/WARN/FAIL,有 FAIL 先处理再往下走;
  2. env:记录每个共享是 CoW 还是 NoCOW、每个子卷的 lastsnap;
  3. gens:采样多个受影响共享,确定 G_ATTACK,确认 G 以下存在代数空档;
  4. 单文件验证:挑一个确知原件的加密文件,plan → extract,本地 SHA-256 完全一致才能继续;
  5. survey:全量只读普查,输出每个文件的状态(exact 可精确恢复 / needs_inference 需推测 / no_candidate 无候选……);
  6. extract + validate:分批导出(先导照片、文档这类最重要的目录),本地多进程验证,最后出分状态统计报告。

适用范围说明:真机验证环境是 DSM 7.2.1(SHR),仓库内附了 DSM 6、OMV、Unraid 等设备的定制引导和报错处理;ext4 不适用(多数 QNAP 卷和群晖老卷),ZFS 需要另一套工具链。

写在最后

这个案例的价值,是证明了一件反直觉的事:没有密钥,勒索病毒也未必是终点。Btrfs 的写时复制”顺便”把原始数据留在盘上,给了受害者最后一搏的机会。但有三点必须想清楚:

  • 这条路线的成立依赖文件系统实现和勒索软件的加密模式——它救得了大部分照片视频,救不了小文件和数据库;
  • 即使”精确恢复”也需要 SHA、校验和、解码、原件对照一套组合拳来证明——校验和逐块一致,才能真正放心;
  • 它永远不是备份的替代品。真正的保险仍然是:快照 + 写入保护、3-2-1 备份、至少一份离线副本。NoCOW 共享能救回来,靠的也只是”恰好做过一次快照”。

而最关键的防御还是上一篇里那句:把 DSM 升到安全版本边界,别把 DSM 端口直接暴露公网。门没被撬开,就永远不需要研究怎么从里面出来。

参考来源:crt106/c3rb3r-btrfs-recovery(GitHub,MIT)、soimy/c3rb3r-rescue、Benjamin-zlt/c3rb3r-baremetal-rescue、TechRounder、GridinSoft、Ciphers Security、奇虎 360 与群晖官方公告。

发表我的评论
取消评论
表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址