区块链桥安全 – 第二部分

区块链安全4个月前发布 admin
41 0 0
登链社区

这是 Bridge 安全系列的第二篇文章。参见简介和第 1 部分

本文解释了以下提到的 2 个漏洞:

  1. 跨链签名重放
  2. 签名重放的变体

1. 跨链签名重放

test_sendMsgPermit_cross_chain_signature_replay()展示了跨链签名重放攻击是如何工作的。

image-20240930222847819.png

跨链签名重放的可视化表示。

在此之前,让我们了解一下 domain separator 的用途:

它用于区分在此合约上和任何其他合约上签署的签名,甚至区分在不同链上签署的签名。通常它包含这些参数:

  1. name:合约的名称。
  2. version:合约的版本。可能会发生同一个项目再次部署其合约的情况,因此版本可能高于旧版本。
  3. chainId:用于区分签名所针对的链的 chainId。
  4. verifyingContract:通常是此合约的地址(address(this))。

让我们来谈谈这个示例测试。

在阅读时请参考图表,以便更直观地理解。

test_sendMsgPermit_cross_chain_signature_replay()中,它简单地创建了一条交易/消息,用于将50e18个 token 转移到目标链上的user地址。然后它对消息进行签名,并通过sendMsgPermit()发送消息。

然后,在链 id 2 上创建 bridge 合约的新实例,称为destBridgeOnChainId2。它还向Alice铸造了100个 token。(铸造这 100 个 token 的原因是目标链上需要有一些金额,因为此处的交易是为了将50个 token 从Alice在目标链上拥有的 token 转移到目标链上的user)。

它检查余额,然后使用executeMessage()BridgeSignatureReplay.t.sol#L214上执行交易。在检查余额后,可以看到两者现在都拥有50个 token。这是正确且预期的。因为Alice签署了交易以转移50个 token。

现在,在BridgeSignatureReplay.t.sol#L222上,恶意用户/攻击者再次使用相同的签名组件和相同的交易结构调用sendMsgPermit()

然后在目标链 bridge 上使用destBridgeOnChainId2.executeMessage()执行相同的交易。

有趣的是,你可以看到最后一个余额断言正在通过,这意味着攻击者能够成功执行跨链重放攻击,并能够从Alice的余额中获得所有 token(总共100个)。

这种攻击之所以成为可能,是因为BridgeSignatureReplay.sendMsgPermit()中注释掉的部分未能分配transaction.srcChainId。因此,即使签名者签署了具有特定srcChainIdtransaction结构(假设为31337,或图表中的链 1),仍然可以在任何其他链上使用该签名,而不是31337(或是最初创建和签名结构的链)。

因此,在合约级别发生的事情是,在BridgeSignatureReplay.sendMsgPermit()中,在创建ethSignedMessageHash时,使用了与 id 为31337的链上相同的transactionHash,并且由于攻击者使用了原始交易结构和正确的签名,因此恢复的签名与transaction.from匹配。

如果BridgeSignatureReplay.sol#L81的赋值发生了(目前没有发生,因为它被注释掉了),则交易将在BridgeSignatureReplay.sol#L89上回滚,因为合约内的transaction结构现在将具有不同的 chain id(不是31337,假设攻击者在不同的链上调用BridgeSignatureReplay.sendMsgPermit(),而不是31337,即创建和签署transaction结构的链),因此transactionHash将会改变,因此ethSignedMessageHash也会改变,最终攻击者使用的签名将无效(因为最初它是为具有srcChainId31337 的不同结构创建的)。

因此,在test_sendMsgPermit_cross_chain_signature_replay()的当前示例中,攻击者可以在多个链上多次使用该签名。不仅仅是在签名者签署消息的链上。这就是攻击者能够在目标链上获得更多 token(100e18),而不是签名者想要转移的 token(50e18)的原因。

为了避免此项目中的此漏洞,可以添加srcChainId赋值,以便创建不同的transactionHash,这将导致跨链重放交易回滚,如上所述。

这种攻击可能存在变体,它不仅与缺少赋值或未使用的链 id 比较有关,而且仅仅与缺少应该存在的东西有关!更多信息如下。

注意:在此测试的BridgeSignatureReplay.t.sol#L219-L222中,注释说“在链 id 3 上的签名重放”,但它仍然使用用于在BridgeSignatureReplay.t.sol#L201上在链 1 上发送消息的bridge实例。实际上,那将是部署在该链 3 上的 bridge,正如注释所说。

2. 签名重放的变体

其他参数的缺失可能导致此类漏洞,特别是包含在 domain separator 中的参数,如 name、version、verifying contract。

假设项目决定升级(或说更新,它会弃用旧合约)一份合约,其中 name、version、verifying contract address(address(this))等内容可能会发生变化。

例如,如果有一个名为XYZProject的项目,它使用带有 EIP712 的 ECDSA。最初的 domain separator 值为

name: “XYZProject”

version: 1

chainId: 111 或是部署合约的链。

verifyingContract: address(0x1XyZprOject) 或任何地址。

为此合约签署的所有签名都将在其 domain separator 中使用这些链下值,然后将其用于创建将要签名的摘要。

在合约升级或说新合约部署之后,这些值可能会发生变化。假设新值为:

name: “NewXYZProject”

version: 2

chainId: 111 或部署合约的任何其他链。

verifyingContract: address(0x1NeWXyZprOject) 或任何其他地址。

因此,现在对于合约内验证,创建的摘要(ethSignedMessageHash)将完全不同(因为 domain separator 值不同),因此为先前合约版本创建的签名将无用,因为先前的签名过去常常使用旧的/初始的 domain separator 值。

因此,签名重放可能存在变体,具体取决于合约是否已更新,以及是否仍使用相同的 domain separator 参数,或者根本不使用这些参数。例如,任何具有签名验证逻辑的合约,如果在更新合约或部署新合约后不注意这些值,都可能容易受到签名重放的攻击,因为先前签署的签名可以与新合约一起使用。

下一部分(第 3 部分)展示了任意调用执行

  • 原文链接:calibersec.com/blockchai…
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
区块链桥安全 - 第二部分

登链社区始于 2017 年,通过构建高质量的技术内容平台和线下空间,助力开发者成为更好的 Web3 Builder。

登链社区

  • 登链社区网站 : learnblockchain.cn

  • 开发者技能认证 : decert.me

  • B站 : space.bilibili.com/581611011

  • YouTube : www.youtube.com/@upchain
登链社区

本篇文章来源于微信公众号: 登链社区

© 版权声明

相关文章