欢迎光临
我们一直在努力

Cosmos EVM 下溢漏洞:从代码层面拆解 570 万美元多链攻击

Cosmos EVM 下溢漏洞:从代码层面拆解 570 万美元多链攻击

2026 年 8 月 20 日到 25 日,Cosmos EVM 共享模块里的一个余额处理漏洞在 MANTRA、TAC、KiiChain 等六条链上被利用,攻击者卷走大约 570 万美元。其中约 287 万通过去中心化交易所转移,285 万通过中心化平台转移,部分资金随后被冻结。

这个漏洞早在 4 月 25 日就被上报,当时评估认为不影响主网,直到第一次攻击前大约 20 小时才把补丁 backport 到受影响的分支。这不是什么高级零日漏洞,而是两个系统对同一个账户有着不同理解,却没人检查它们是否一致。

根因:两个账本,一个账户

Cosmos EVM 跑在 Cosmos SDK 上面。EVM 这边的 StateDB 只记录一个数:可花费余额。Cosmos SDK 的 x/bank 模块记录两个:可花费余额和锁定余额(比如 vesting)。

一个 vesting 账户可以有一部分代币是锁定的。x/staking 和 staking 预编译合约都允许把锁定的代币委托出去。委托的金额是从总额里扣,不是只扣可花费的部分。

问题就出在这儿。委托完成后,代码会把委托后的余额写回 EVM 的 StateDB。它用 EVM 里那个更小的可花费余额去减掉全部委托金额。这个减法没有做检查。

假设一个 vesting 账户有 100 可花费,900 锁定,然后委托 500。EVM 层执行的是 100 减 500。在 Go 的无符号整数运算里,这不会报错,而是回绕成大约 2²⁵⁶ 减 400。

这个回绕值就是整个攻击的核心。攻击者在 EVM 状态里突然拥有了天文数字的余额。

为什么对账逻辑会把漏洞变成武器

Cosmos EVM 不是简单把 StateDB 的余额复制到 x/bank。它会做对账。对账时,正 delta 会 mint 代币,负 delta 会 burn 代币。

所以攻击者有两个操作:

  • 从回绕账户里转出有限金额,触发在真实 Cosmos 账本上 mint,用来支撑 EVM 余额。
  • 给受害者账户发送 2²⁵⁶ 减去对方实际余额的数值,让对账计算出负 delta,从而 burn 掉受害者的真实持仓。

第二个操作才是最让人后背发凉的。攻击者不需要偷私钥,不需要治理投票,只需要一个无需许可的 vesting 账户,以及一笔比可花费余额多委托 1 wei 的交易。

这个攻击要求链上允许无需许可创建 vesting 账户,很多 Cosmos EVM 链都开了这个功能。受影响的版本是 0.6.2 之前的所有版本,以及 0.7.0 到 0.7.1。

两条版本线的失败方式不同,但同样致命。在 0.6.x 链上,对账时在底层 SDK 账本上 mint 会导致供应量溢出,链直接停掉。在 0.7.x 链上,余额是直接写进 x/bank 的,uint256 到 int256 的转换之后变化仍然保留,也就是说虚增的余额会一直存在,而且能花。

两条路径都在一笔交易里完成,净供应量变化为零,发起的合约部署在一个预先算好的地址上,而这个地址先被转成了 vesting 账户。

代码修复:减法之前先检查

有漏洞的代码路径在执行委托写回时,没有验证可花费余额是否足够减。修复版本 v0.6.2 和 v0.7.2 加了一个保护,防止下溢。

有漏洞的模式大概是这样:

// 有漏洞:未检查的减法
func (s *StateDB) ApplyDelegation(addr common.Address, amount *big.Int) {
current := s.GetBalance(addr)
newBalance := new(big.Int).Sub(current, amount)
s.SetBalance(addr, newBalance)
}

修复后的版本:

// 修复后:防止下溢
func (s *StateDB) ApplyDelegation(addr common.Address, amount *big.Int) error {
current := s.GetBalance(addr)
if current.Cmp(amount) < 0 {
return fmt.Errorf("delegation amount exceeds spendable balance")
}
newBalance := new(big.Int).Sub(current, amount)
s.SetBalance(addr, newBalance)
return nil
}

这一个检查的效果是:交易直接 revert,而不是产生回绕余额。没有 mint,没有 burn,也没有对账 delta。

但光加一个检查还不够。事后报告确认修复至少涉及三个 commit,包括独立锁定 vesting 余额快照,以及一个模块账户防护。原因很微妙:即使减法被保护了,对账层仍然必须区分“EVM 余额因为正常转账而减少”和“EVM 余额因为委托动了锁定代币而减少”。后者不应该在 Cosmos 账本上触发 burn。

正确的架构修复是把委托金额和喂给对账的可花费余额 delta 隔离开。委托写回应该把变化记录成 staking 操作,而不是 EVM 余额变更,这样对账步骤永远不会在可花费那一侧看到负 delta。

代码之外出了什么问题

代码 bug 本身很直接。真正把一个可修补的缺陷变成多链事件的是响应过程。

  • Cosmos Labs 在 2026 年 4 月 25 日收到漏洞报告,最初判断只影响非 18 位小数的网络,并认为对生产资金没有风险。
  • 到 8 月 13 日,团队确认所有 Cosmos EVM 链都受影响,与小数位配置无关。
  • 修复在 5 月 15 日就已经进了主分支,但 backport 版本 v0.6.2 和 v0.7.2 直到 8 月 19 日 23:01 UTC 才发布。
  • 第一次攻击在 8 月 20 日 19:06 UTC 打中 MANTRA,距离补丁发布大约 20 小时。
  • 补丁说明没有点出这个漏洞。链运营方根本不知道这是一个紧急安全修复,而不是普通更新。

MANTRA 的事后报告指出,20 小时的窗口对于组织一次破坏状态的升级来说完全不现实,因为验证者集合里都是独立的网络运营方,需要协调。

Cosmos Labs 自己的静默补丁政策写着:“当问题构成立即或全网风险时,Cosmos Labs 会启动紧急缓解、私有修复分发或协调升级,然后再公开披露。”团队因为早期测试认为生产链安全,就把这个漏洞当成静默补丁处理。这个判断错了,而且一旦 8 月 13 日确认所有链都受影响,政策本身的标准就应该覆盖这个判断。

这对 Cosmos 生态意味着什么

这个漏洞并不新颖。它是两个模块之间的类型不匹配:它们共享一个账户,但对这个账户持有什么的定义不同。EVM 认为账户只有一个余额。SDK 知道账户有两个。对账层信任了 EVM 的视角,却没有检查 SDK 的锁定余额是否让这个视角不完整。

v0.6.2 和 v0.7.2 的修复堵住了下溢。更深层的教训是关于验证。一个 if current.Cmp(amount) < 0 检查就能避免 570 万美元的损失。一次对受影响链运营方的私下通知就能给他们时间打补丁。

还在跑 0.6.0、0.6.1、0.7.0、0.7.1 的链:要么升级,要么停机。补丁已经在了。攻击路径已经公开。从补丁可用到被利用的窗口,已经不再以周为单位了。

对于在共享基础设施上开发的开发者:每一个跨模块边界的余额变更都是一个潜在的对账 bug。把检查写上。测试下溢。当安全团队说“我们在自己的配置上复现不了”的时候,问一句:攻击者会不会用不同的配置。

赞(0)
未经允许不得转载:171主机测评 » Cosmos EVM 下溢漏洞:从代码层面拆解 570 万美元多链攻击
分享到: 更多 (0)

评论 抢沙发

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