| 时间 | 2024 年 10 月 16 日 |
| 涉案 | 约 $5000 万 |
| 模式 | ⚠️ 与三个月前的 WazirX 高度相似 |
| 类别 | C7 密钥与签名流程 |
一、背景:一次"没什么特别"的例行操作
Radiant 是一个跨链借贷协议,其关键权限由多签控制。攻击发生时,团队正在执行一次常规的多签操作。
⚠️ 攻击者早已在多个签名者的设备上植入了恶意软件。
⟹ 他们不需要发起任何交易,
⭐ 只需要【等待】团队自己发起一次多签操作,
然后在签名的那一刻把载荷换掉。
⭐ 这是一种"寄生"式的攻击:攻击者不制造异常事件,他利用一个本来就会发生的正常事件。
二、⭐ 复盘里最重要的那句话
Radiant 的事后复盘提到了一个细节:
签名过程中出现了交易失败、需要重新签名的情况—— 而这在团队看来是"多签钱包的常见现象"。
⭐ 这句话值得反复读。
攻击的实际过程需要:
· 恶意载荷被签名
· ⚠️ 而正常载荷的交易会失败
⟹ 于是签名者会看到"这笔交易失败了,请重签"
⭐ 这是一个【极其明确】的异常信号。
⚠️ 但它没有触发任何警觉,
因为团队【已经习惯了】偶尔的签名失败。
⭐异常的常态化(normalization of deviance):
一个系统里反复出现的小毛病,如果每次都"没出事", 就会逐渐从"异常"被重新分类为"正常"。 而当它某一次真的是攻击信号时,没有人会注意到。
三、为什么这个模式会重复出现
① 攻击者在复用一套已被验证的工具
⭐ WazirX(2024-07)⟹ Radiant(2024-10)⟹ [Bybit(2025-02)](https://blog.ifcalm.org/posts/security/blockchain/25-bybit/)
三起事故的核心是同一件事:
【让签名者签署他没有看到的内容】
⚠️ 而且金额在【递增】:2.35 亿 ⟹ 5000 万 ⟹ 14.6 亿
⟹ 这说明攻击方在【迭代和复用】,
⭐ 而防守方在各自为战 ——
Radiant 出事时,WazirX 的复盘已经公开三个月了。
⭐ 一条实践:把同行的事故复盘当作自己的待办清单。每一份公开复盘发布后,问一句:"我们有没有同样的问题?"——这件事的成本极低,而 Radiant 本可以因此避免。
② 攻击面在"日常运维"里,而不在"上线时"
⚠️ 安全投入几乎全部集中在【上线前】:
审计、测试、赏金。
⭐ 而这类攻击发生在【上线后的第 500 次例行操作】时。
⟹ 没有人为"第 500 次例行操作"做威胁建模。
③ 恶意软件驻留期长
攻击者在设备上潜伏了相当长的时间:
· 观察团队的操作习惯
· 等待一次足够有价值的多签操作
· ⭐ 在最合适的时机才动手
⚠️ 这意味着"我们最近没发现异常"完全不构成证据 ——
⭐ 一个好的攻击者的定义就是"你发现不了他"。
四、防御
① 把"小毛病"当作信号来管理
⭐ 建立一条纪律:任何【计划外】的事件都必须被记录和解释。
□ 交易失败了 ⟹ 为什么?失败原因是什么?
□ 界面显示和预期不一致 ⟹ 停下来,查清楚
□ ⚠️ 需要重新签名 ⟹ ⭐ 视为【安全事件】,
而不是"再点一次"
□ 某台设备行为异常 ⟹ 立刻从签名流程中隔离
⟹ ⭐ 关键是把默认反应从"再试一次"改成"先搞清楚"。
⭐ 一个具体做法:维护一份"异常台账"。每一次计划外事件都记一行:时间、现象、解释、是否已确认根因。当同一个现象出现第三次而根因栏还是空的,那就是一个必须被处理的风险。
② 签名操作用专用的、隔离的设备
⭐ 一台"签名专用机"应该:
□ 不用于收发邮件、浏览网页、装任何其他软件
□ ⭐ 系统从只读介质启动,每次使用后重置
□ 与办公网络物理隔离
□ ⚠️ 校验 calldata 的那台设备,
必须和发起交易的那台【不是同一台】
⟹ 恶意软件的立足点被消除之后,
[第 23 篇](https://blog.ifcalm.org/posts/security/blockchain/23-wazirx/)的整个攻击链就断了。
③ 多人独立校验,且结果要交叉比对
⭐ 不要让所有签名者用同一个流程:
签名者 A:用工具 X 解析 calldata
签名者 B:⭐ 用工具 Y 独立解析
⟹ 两人报出的【解析结果】必须一致
⚠️ 如果所有人都用同一个前端,那么
"多人校验"只是把同一个错误看了多遍。
④ 假设终端已被攻破,然后设计
⭐ 把"我们的设备可能已经被入侵"当作【前提】而不是【风险】:
□ 大额转账只能转到白名单地址
⟹ 即使签了恶意交易,钱也去不了攻击者的地址
□ ⭐ 时间锁 ⟹ 恶意交易签出后还有 N 小时可以撤销
□ 独立的监控(跑在另一套基础设施上)
⟹ 检测到异常自动触发暂停
□ ⚠️ 定期轮换密钥与设备
⭐⭐ 这是第 23 篇那条结论的强化版: 不要依赖"人会看仔细",也不要依赖"我们的设备是干净的"。 设计要能在这两条都不成立时仍然保护资金。
五、⭐ 举一反三
核心命题一:异常的常态化
⭐⭐ 安全事故最可靠的前兆,是一个被大家习惯了的小毛病。
⚠️ 在你的系统里找找这些"大家都知道、没人当回事"的现象:
· "这个告警经常误报,忽略就行"
⟹ ⭐ 那么真的告警来的时候呢?
· "这个测试偶尔会挂,重跑一下"
⟹ 它可能在告诉你一个真实的竞态
· "那个余额对不上一点点,正常"
⟹ ⭐ 一点点是多少?为什么?
· "这个接口有时候超时,重试就好"
· "签名有时候失败,重签" ⟹ 本篇
· "部署脚本第一次总是失败"
⭐ 判据:任何你能说出"这个一直这样"的现象,
而你【说不出根因】—— 那就是一个未被理解的风险。
核心命题二:把同行的复盘当作自己的待办
⭐ 一条低成本、高回报的流程:
每当一份公开的事故复盘发布:
① 读完
② ⭐ 逐条问:"我们有没有同样的问题?"
③ 把"有"的那几条变成工单
④ ⚠️ 把"没有"的那几条写下理由 ——
因为半年后情况可能变了
⟹ Radiant 出事时,WazirX 的复盘已经公开三个月。
⭐ 这个档案本身就是为这个用途准备的——上线前检查清单把 29 起事故压缩成了一份可以逐条打勾的表。
核心命题三:为"第 500 次操作"做威胁建模
⭐ 安全评估通常覆盖"系统上线"这个时刻,
⚠️ 而攻击往往发生在"日常运维"中。
⟹ 需要单独建模的运维场景:
□ 例行的参数调整
□ ⭐ 例行的多签操作(本篇、[第 23 篇](https://blog.ifcalm.org/posts/security/blockchain/23-wazirx/)、[第 25 篇](https://blog.ifcalm.org/posts/security/blockchain/25-bybit/))
□ 合约升级
□ 新增/下架资产
□ 密钥轮换(⚠️ 轮换过程本身是一个窗口)
□ 人员入职/离职
□ ⭐ 应急响应演练本身(它需要高权限)
六、本案小结
- 攻击手法与三个月前的 WazirX 几乎一样:多个签名者的设备被植入恶意软件,前端显示正常而签出的是攻击载荷。
- ⭐ “寄生"式攻击:攻击者不制造异常事件,他等待并利用一个本来就会发生的正常事件——一次例行的多签操作。
- ⭐复盘里最重要的一句:签名失败需要重签,在团队看来是"多签钱包的常见现象”。 这是一个极其明确的异常信号,但它没有触发任何警觉,因为团队已经习惯了偶尔的签名失败。 -⭐ 异常的常态化:一个反复出现的小毛病,如果每次都"没出事",就会从"异常"被重新分类为"正常"——而当它某一次真的是攻击信号时,没有人会注意到。
- ⚠️ WazirX(7 月)→ Radiant(10 月)→ Bybit(次年 2 月),金额递增:攻击方在迭代复用一套已验证的工具,而防守方在各自为战——Radiant 出事时 WazirX 的复盘已公开三个月。
- 把默认反应从"再试一次"改成"先搞清楚":维护一份异常台账,同一现象出现第三次而根因栏还是空的,就是一个必须处理的风险。
- 签名专用机:不收邮件、不上网、只读介质启动、与办公网物理隔离,且校验 calldata 的设备不能是发起交易的那台。
- ⚠️ 多人校验必须用不同的工具——所有人用同一个前端时,“多人校验"只是把同一个错误看了多遍。 -⭐ 假设终端已被攻破,然后设计:白名单地址、时间锁、独立基础设施上的监控。不要依赖"人会看仔细”,也不要依赖"我们的设备是干净的"。
- 安全评估覆盖的是"上线",而攻击发生在"第 500 次例行操作"——运维场景需要单独建模。
思考题
- 为什么说这是"寄生式攻击"?它和主动发起交易的攻击相比,在检测上有什么不同?
- “签名失败需要重签"为什么是一个明确的异常信号?请解释攻击过程中它为什么会出现。
- 在你的系统里找出三个"这个一直这样、但说不出根因"的现象。逐个尝试解释。
- 设计一份"异常台账"的字段。什么条件下一条记录应该升级为安全事件?
- 一台"签名专用机"的完整配置要求是什么?请写出一份可执行的清单。
- 为什么"校验 calldata 的设备必须和发起交易的不是同一台”?如果是同一台会怎样?
- “多人校验必须用不同的工具”——请设计一个具体方案,说明两个人分别用什么。
- 在"我们的设备已经被入侵"这个前提下重新评估你的多签流程。哪些防御还站得住?
- 设计一个"读同行复盘 → 转成工单"的流程。谁负责?多久一次?产出是什么?
- 列出五个需要单独做威胁建模的运维场景,为其中一个写出完整的威胁模型。