Nomad Bridge攻击事件:根本原因分析

Nomad的Replica合约存在一个实现缺陷,导致无法正确验证消息的身份。

高级问题

一个实现上的 bug 导致 Replica 合约 无法正确验证消息。这个问题允许伪造任何消息,只要它还没有被处理。因此,依赖 Replica 来验证入站消息的合约遭受了安全故障。这种身份验证失败导致欺诈性消息被传递到 Nomad BridgeRouter 合约。

详情

可接受的根

Nomad 在 Merkle 树(称为“消息树”)中提交跨链消息。这棵树的根通过乐观机制传播到远程链。

Replica 合约使用 mapping(bytes32 => uint256) 跟踪来自其他链的根。这会将根映射到它们变为有效的时间戳。消息可能不会在根的乐观计时器经过之前被处理。当读取映射时,如果条目尚未设置,则会读取默认值(也称为 0 值)。uint256 的默认值为 0。因此,任何未被证明的根在此映射中都将具有 0 的时间戳。

在根传播到另一个链后,消息包含在树中通过 Merkle 证明来证明。消息在其下被证明的根存储在 Replica 合约的 mapping(bytes32 => bytes32) 中。这会将消息的哈希值映射到其下被证明的根。在这种情况下,bytes32 的默认值为 bytes32(0)。因此,任何未被证明的消息在此映射中都将具有 bytes32(0) 的根。

当消息被提交到 process 函数时,协议从映射中读取根,并检查 acceptableRoot 函数是否返回 true。此函数旨在仅当有效根的乐观超时时间已结束时才返回 true。此函数检查根的特殊旧值(来自较旧的系统版本),以及尚未被证明的根(在根映射中具有 0 时间戳)。它通过针对块时间戳检查来确保根的计时器已过 return block.timestamp >= _time;

function acceptableRoot(bytes32 _root) public view returns (bool) {
    // this is backwards-compatibility for messages proven/processed
    // under previous versions
    // 这是对在先前版本下证明/处理的消息的后向兼容性
    if (_root == LEGACY_STATUS_PROVEN) return true;
    if (_root == LEGACY_STATUS_PROCESSED) return false;

    uint256 _time = confirmAt[_root];
    if (_time == 0) {
        return false;
    }
    return block.timestamp >= _time;

初始化器

Replica 在其关联的 Home 合约之后部署时,Replica 合约会使用特定状态进行初始化。这确保了新部署不必重放来自远程 Home 的所有过去更新才能处理消息。部署者可以传递一个 _committedRoot,消息树的历史记录开始接收更新。

在初始化器期间,confirmAt[_committedRoot] 设置为 1。这确保了包含在其初始根中的消息可以被处理。这允许新初始化的 Replica 接收早于其部署的消息。

但是,如果在 Replica 与其对应的 Home 合约同时部署的情况下(就像最初部署时一样),Home Merkle 树不包含任何消息。在 Nomad 实现中,没有叶子的 Merkle 树的根为 bytes32(0)。因此,与对应的 `Home` 合约同时部署的 Replica 将使用 bytes32(0) 的根进行初始化,并且 confirmAt[bytes32(0)] 将设置为 1

function initialize(
    uint32 _remoteDomain,
    address _updater,
    bytes32 _committedRoot,
    uint256 _optimisticSeconds
) public initializer {
    __NomadBase_initialize(_updater);
    // set storage variables
    // 设置存储变量
    entered = 1;
    remoteDomain = _remoteDomain;
    committedRoot = _committedRoot;
    // pre-approve the committed root.
    // 预先批准提交的根。
    confirmAt[_committedRoot] = 1;
    _setOptimisticTimeout(_optimisticSeconds);
}

结合

这是 acceptableRoot相关行

uint256 _time = confirmAt[_root];
if (_time == 0) {
    return false;
}
return block.timestamp >= _time;

逐行:

uint256 _time = confirmAt[_root];

首先,将 confirmAt[_root] 加载到一个名为 _time 的变量中。对于未知消息(包括伪造消息),此 _root 等于 bytes32(0)。在任何以 _committedRoot 设置为 bytes32(0) 初始化的 Replica 上,confirmAt[bytes32(0)] 的值在初始化时设置为等于 1

if (_time == 0) {
    return false;
}

因为 _time 是 1,所以我们跳过这个块。

return block.timestamp >= _time;

_time1,因此任何有效的块时间戳都将大于或等于 1。因此,acceptableRoot(bytes32(0)); 始终为这些 Replica 合约返回 true

这允许未经证明的消息通过 process 函数中的以下检查。反过来,这允许消息无需先被证明即可被处理。

function process(bytes memory _message) public returns (bool _success) {
    // ...
    require(acceptableRoot(messages[_messageHash]), "!proven");
    // ...
}

常见问题解答

此代码何时引入?

相关代码于 2022 年 6 月 21 日在智能合约升级中引入。

以前,process 函数中的 require 语句编写如下:

function process(bytes memory _message) public returns (bool _success) {
    // ...
    require(messages[_messageHash] == MessageStatus.Proven, "!proven");
    // ...
}

之前的 require 语句要求 messages 映射中有一个非 0 值。这只能通过先前对 prove 的调用来设置。更改后,可以从映射中加载 0 值,并将其传递给 acceptableRoot

此更改是消息处理语义的更大行为更改的一部分。具体来说,旧的 require 语句会永久阻止在无效证明的情况下处理有效消息。较新的 require 语句可防止欺诈更新程序通过提供无效证明来阻止有效消息的传递。为了实现这一点,我们引入了上述“可接受的根”部分中描述的消息到根的映射。

Quantstamp 对 Replica 合约升级的审计细节是什么?

此 Nomad 系统升级已于 2022 年 5 月和 6 月初由 Quantstamp 审核。6 月 9 日收到了最终报告。此更改是在审核期间的5 月 26 日进行的,作为与审核相关的清理和增强(特别是与 QSP-2 和 QSP-34 相关),并且包含在为修复后重新审核提供的提交哈希中。

Nomad 还通过 ImmuneFi bounty 激励公众审查代码,该奖励自 6 月 9 日起生效。此问题未通过奖励系统报告。

此更改何时在链上进行?

生产环境已于 2022 年 6 月 21 日在以下交易中升级:

为什么 Nomad Watchers 没有采取行动?

Nomad Watchers 观察并响应 Updater 密钥的泄露,但尚未观察智能合约 bug 引起的可疑活动。由于漏洞存在于智能合约的 process 函数中,因此消息不需要欺诈性的 Updater 签名,因此不会触发 Watchers。因此,没有 Watchers 采取行动。

系统的当前状态是什么?

副本已从 Nomad Bridge 和 Nomad Governance 合约中取消注册。这可以防止 Bridge 处理任何消息。这减轻了流程函数中漏洞的影响。新的桥交易仍然可以启动,但是无法处理。 NomadBridge GUI 已被禁用。我们建议用户在部署完整修复程序并执行系统重启之前,不要尝试桥接。

  • 原文链接: medium.com/nomad-xyz-blo...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
blog_nomad
blog_nomad
江湖只有他的大名,没有他的介绍。