在过去一周(2026/07/27 - 2026/08/02),以下 2 起重大安全事件受到关注,合计造成约 $88M 的损失。
| 日期 | 事件 | 类型 | 预估损失 |
|---|---|---|---|
| 2026/07/29 | LULA | 业务逻辑缺陷 | ~$578K |
| 2026/07/30 | COLDCARD | 熵生成缺陷 | ~1,370 BTC (~$88M)* |
* COLDCARD 的损失因确认方式不同而有所差异:所示的 ~1,370 BTC (~$88M) 为可公开验证的链上最低值(coldcardwatch.com);通过私下渠道核实(Galaxy Research,来自与 73 名受害者的通信)的数字更高,约为 1,596 BTC,若将疑似但未经确认的转移纳入统计,则高达 ~2,055 BTC (~$130M)。
入选原因
- LULA:一个可操控 AMM 交易对余额并强制重新同步储备金的特权代币函数,通过价格操控成为可反复利用的流动性耗尽原语。
- COLDCARD:钱包固件中的构建与集成错误,导致种子生成过程静默地路由至确定性软件回退路径,破坏了熵保证,使种子恢复变成一个可离线搜索的问题,并最终演变为大规模资金损失。
Web3 领域最佳安全审计机构
在上线前验证设计、代码和业务逻辑
每周重点:COLDCARD
我们选择 COLDCARD 作为本周重点,因为一个钱包熵缺陷造成了本期最大损失。根本原因——一个构建守卫检查的是配置宏是否存在,而非是否已启用——是功能测试无法捕捉到的静默集成错误,这一教训适用于任何链上安全依赖于链下随机性的系统。
COLDCARD 是一款比特币硬件钱包,其于 2021 年发布的固件在生成钱包种子时,使用的是确定性软件来源,而非预期的硬件随机数生成器(RNG)[1][2]。该缺陷直至 2026 年 7 月下旬才被大规模利用,受影响的钱包于 7 月 30 日开始遭到链上批量清空:公开确认的损失至少为 1,370 BTC(约 $88M,以 8 月 5 日 $64,099 的价格计算)[3],而私下确认的报告将这一数字提高至约 1,596 BTC [4]。根本原因是一个构建与配置错误,将种子生成路由到了软件回退路径,而非预期的硬件 RNG。对于受影响的设备,这使得种子恢复从密码学上不可行的问题变成了一个离线搜索问题。
背景
COLDCARD 是一款比特币硬件钱包。钱包的私钥和地址均派生自一个单一的秘密值——种子,因此任何自托管钱包的安全性取决于该种子的两个属性:保密性以及生成时的不可预测性。硬件钱包的存在主要是为了保护前者;而本次事件是后者的失败。BIP-39 助记词对人类可读,但不可预测性要求背后的安全属性,仍然是用于创建它的随机字节的熵。如果这些字节可以被复现,种子就可以被复现。直觉上,这就像一个保险箱,其密码是通过掷骰子来选择的:如果骰子被做了手脚,那么任何知道偏差的人都可以打开保险箱,无论锁有多坚固。
对于钱包来说,"做了手脚的骰子"意味着弱随机数生成器。硬件钱包预期从其安全微控制器上的硬件真随机数生成器(TRNG)获取种子熵,因为软件伪随机数生成器(PRNG)是确定性的:给定其内部状态和调用历史,其输出可以被精确重放。如果生成器的输出是可预测的,其候选范围就可以被枚举,并与公开钱包数据(如地址、xpub 或公钥)进行匹配,从而将种子恢复从密码学上不可行的问题降格为离线搜索。
COLDCARD 的固件暴露了两个独立的 RNG 接口。MicroPython 附带了一个 STM32 平台层,该层暴露了钱包加密库所需的全局 rng_get() 符号,而 COLDCARD 也维护着自己的板级本地硬件 RNG 封装。两个接口均旨在提供硬件熵,钱包的加密库通过固件构建时解析的单一全局 RNG 符号访问其中之一。
漏洞分析
根本原因是围绕 MICROPY_HW_ENABLE_RNG 配置宏的构建与集成错误。COLDCARD 的生产板配置将此宏设置为 0,因为固件本意是使用其自己的板级本地硬件 RNG 封装,而非 MicroPython 的硬件 RNG 实现。然而,钱包生成路径已迁移至 ngu.random.bytes(32),而加密库的 STM32 路径最终依赖于由 MicroPython 解析的全局 rng_get() 符号。
问题链如下:
generate_seed()
-> ngu.random.bytes(32)
-> libngu CHIP_TRNG_32()
-> rng_get()
-> MicroPython STM32 RNG 模块
-> 由于 MICROPY_HW_ENABLE_RNG == 0,回退至 Yasmarang 软件实现
板级配置禁用了 MicroPython 的硬件 RNG 分支:
// We have our own version of this code.
#define MICROPY_HW_ENABLE_RNG (0)
加密库仍将该宏视为存在硬件 RNG 的充分证明 [5],因为它在调用 rng_get() 之前仅检查宏是否存在:
extern uint32_t rng_get(void);
#define CHIP_TRNG_32() rng_get()
#ifndef MICROPY_HW_ENABLE_RNG
#error "get a HW TRNG plz"
#endif
该守卫遗漏了危险情况:一个被定义为 0 的宏仍然是已定义的,因此 #ifndef 检查通过,构建成功。由于 MicroPython 根据宏的值而非其存在性来选择 RNG 实现,MICROPY_HW_ENABLE_RNG == 0 将 rng_get() 路由到了软件回退分支,而非 COLDCARD 的板级本地封装 [6]:
#if MICROPY_HW_ENABLE_RNG
// STM32 硬件 RNG
#else
// Yasmarang 软件回退
#endif
不同设备代次的影响有所不同。对于搭载 v4.0.0-v4.1.9 固件的 Mk2/Mk3,Block 的分析 [7] 指出,没有任何密码学熵被添加到 ngu.random,因此一旦回退状态和调用历史已知,钱包生成就可能变得确定性(Coinkite 的公告 [2] 将 Mk2/Mk3 的范围略微收窄,定为 v4.0.1-v4.1.9)。对于 Mk4/Q/Mk5,安全元件材料被哈希处理,但仅有四个字节被传入 ngu.random.reseed(),将安全重播种限制在单个 32 位状态字,远少于用户所期望的钱包熵搜索空间。对最终 32 个随机字节进行哈希处理无法增加结果的熵;它只是对一个已经受限的候选集进行变换。
攻击分析
与智能合约漏洞利用不同,本次事件没有单一的链上攻击交易可供追踪。这是一个离线种子恢复问题,随后在链上进行了批量清空。前提条件是,受影响用户已通过存在漏洞的 ngu.random.bytes(32) 路径生成了他们的钱包种子,因此其种子材料依赖于可复现的软件回退状态,而非完整的硬件熵。第二个推断出的前提条件是,受影响的钱包仅凭种子即可派生:强大且唯一的 BIP-39 密语通过 PBKDF2 混入了独立的、由用户提供的熵,这部分熵从未被 RNG 缺陷触及,使得此类钱包超出了纯种子枚举的范围;而大规模清空的规模表明,大多数受影响用户没有设置此类密语。在此基础上,恢复过程可能经历了三个步骤:
- 攻击者利用设备元数据、启动时序、RTC/SysTick 假设以及合理的 RNG 调用历史,对候选 RNG 状态进行了约束或枚举。
- 对于每个候选状态,攻击者派生候选钱包种子,并将其与地址、xpub 或生成的公钥等公开钱包数据进行离线匹配。
- 一旦某个候选值与真实钱包匹配,攻击者便恢复种子、重建私钥,并清空相关 BTC。
在链上,盗窃表现为从 7 月 30 日开始的一波地址清空浪潮。独立的链上启发式追踪 [3] 识别出数波清空操作,从 4,580 个已验证地址中合计至少清空了 1,370 BTC(约 $88M,以 8 月 5 日 BTC 价格 $64,099 计值),这是已验证的最低值,而非总量。与此同时,一个私下渠道 [4](通过与 73 名受害者的通信确认)将这一数字提高至约 1,596 BTC,若纳入疑似但未经确认的转移,则升至 2,055 BTC(约 $130M)。
结论
COLDCARD 事件是一次熵生成失败:安全关键的种子生成 API 静默地解析为确定性软件 PRNG 回退,而非预期的硬件 RNG,原因在于构建守卫检查的是配置宏是否存在,而非是否已启用。对于受影响的设备,这使得种子恢复从密码学上不可行的问题变成了离线搜索,最终导致超过 1,370 BTC [3] 在多波操作中被清空(私下渠道的统计将其定为约 1,596 至 2,055 BTC [4])。
核心工程失误在于,已发布的固件从未证明其最关键的安全 API 实际上到达了预期的硬件 RNG。以下三种实践本可发现该问题:用于密码学熵的构建守卫必须同时检查宏的存在性和宏的值;熵回退必须以失败关闭(fail closed)的方式运作,而非静默替换为软件 PRNG;对最终固件镜像的验证应覆盖符号来源和端到端熵流,而不仅仅是代码能否编译通过。受影响的种子无法原地修复;其资金应转移至使用已修复固件创建的钱包,而设置强大且唯一的密语可在不修复种子的情况下降低即时风险 [2]。
这种失败模式值得特别强调:此类随机性缺陷对功能测试是不可见的,因为每个生成的种子在个体上都是有效的。缺陷不在于任何单个输出,而在于产生它们的来源:由于该来源是可预测且可复现的,这些种子整体上落入了一个小的、可枚举的范围内。链下密钥生成组件理应受到一流的安全审查。
参考资料
- [1] BlockSec Phalcon 的警报,将 COLDCARD 钱包清空事件与弱种子生成随机性相关联
- [2] Coinkite,熵问题技术深度解析
- [3] coldcardwatch.com,Coldcard 清空监测:链上被清空地址追踪与方法论
- [4] Galaxy Research,COLDCARD 黑客事件损失估算(通过受害者报告确认约 1,596 BTC)
- [5] libngu,STM32 RNG 路径引用全局
rng_get(),且仅以#ifndef MICROPY_HW_ENABLE_RNG作为守卫 - [6] Coldcard MicroPython,当硬件 RNG 被禁用时,
rng_get()回退至 Yasmarang - [7] Block Engineering,COLDCARD 固件中的可预测 RNG 回退与 32 位重播种问题
本周更多事件
LULA
LULA 是 BNB Chain 上的一个 BEP-20 代币,于 2026 年 7 月 29 日因其代币合约中的业务逻辑缺陷损失约 $578K。攻击者可通过一条可访问的路径触发其特权函数 recycle(),使 Rental 合约将 LULA 直接从 PancakeSwap V2 交易对中转出,然后调用 sync() 将交易对的储备金更新为被操控后的余额。攻击者反复触发 recycle(),将交易对的 LULA 储备金压缩至接近零,随后用少量 LULA 换回了交易对中几乎全部的 USDT [1]。
背景
LULA 是 BNB Chain 上的一个 BEP-20 代币,具有基于租赁的团队奖励机制。符合条件的地址在 Rental 合约中累积待领取的团队奖励,并通过 claimTeamReward() 进行领取。在领取流程中,Rental 合约调用代币的 recycle() 函数以获取用于奖励分发的 LULA。recycle() 不能被任意用户调用;仅有 Rental 合约被授权执行该函数。
claimTeamReward() 入口点包含一个"仅限 EOA"检查。它支持来自外部拥有账户的直接调用(其中 msg.sender == tx.origin),同时也通过检查委托代码前缀来支持 EIP-7702 委托调用。

在自动化做市商(AMM)中,交易对根据其存储的储备金对交换进行定价,这些储备金通过交易对的 sync() 函数进行更新,该函数将储备金设置为交易对的当前代币余额。储备金通常随真实交易而变动,因为它们会随着交换和流动性事件移动,但交易对的代币余额也可以通过直接转账来改变,而 sync() 会将当前余额——无论是否被操控——复制到存储的储备金中。
漏洞分析
根本原因在于 LULA.recycle() 允许 Rental 合约将 LULA 直接从 PancakeSwap V2 交易对中转出,然后调用 sync() 将交易对的储备金更新为被操控后的余额 [1]。

由于 sync() 将储备金设置为交易对中剩余的 LULA 余额,这条特权路径可以将交易对的 LULA 储备金任意压低,同时保持 USDT 一侧不受影响。一旦 LULA 储备金接近零,交易对就会将少量 LULA 定价为几乎等同于其全部 USDT。
攻击分析
以下分析基于交易 0xa219ab9...411d7c。
- 步骤 1:攻击者通过积累约 1.9705 亿 USDT 为操控提供资金。资金来源于多个闪电贷和借贷来源,包括 Moolah/Lista、Aave V3、Venus、PancakeSwap V3、PancakeSwap Vault、Uniswap V4 PoolManager 和 Uniswap V3。
- 步骤 2:攻击者使用约 1.9705 亿 USDT,通过 PancakeSwap V2 路由器执行了一笔大额
USDT -> LULA兑换。这使交易对的 LULA 储备金从约 800 万 LULA 急剧减少至 24,022 LULA,同时将 USDT 一侧增加至约 1.9764 亿 USDT。 - 步骤 3:攻击者通过多个 EIP-7702 钱包调用奖励路径。每个钱包在 Rental 合约上调用
claimTeamReward(),进而触发LULA.recycle(),将交易对的 LULA 储备金从 24,022 LULA 压缩至 0.004 LULA,而此时交易对仍持有大量 USDT。 - 步骤 4:攻击者通过路由器执行最后一笔 PancakeSwap V2 兑换,仅向交易对注入约 4,749 LULA,收到约 1.9764 亿 USDT。
- 步骤 5:攻击者偿还所有闪电贷,获利约 $578K。
结论
BNB Chain 上的 LULA 代币因其代币合约中的业务逻辑缺陷被利用,损失约 $578K:攻击者可通过一条可访问的路径触发其特权函数 recycle(),使 Rental 合约将 LULA 直接从 PancakeSwap V2 交易对中转出,然后调用 sync() 将交易对的储备金重新同步为被操控后的余额。攻击者反复触发此操作以扭曲交易对价格,并用少量 LULA 换回了几乎全部 USDT。
代币合约绝不应暴露可以移动 AMM 交易对余额并强制储备金重新同步的特权路径,因为这样做会将价格控制权交给任何能够访问该路径的人。与 AMM 交易对集成的代币必须确保交易对的储备金仅随真实的、由市场驱动的余额变化而更新,任何读取流动池储备金进行定价的逻辑都应将其视为可被操控的,而非权威可信的。



