Poly Network 攻击的进一步分析

深入探讨以太坊上的攻击流程,揭示一次跨越本体、Poly 和以太坊链的跨链漏洞利用。

Poly Network 攻击的进一步分析

该攻击包含两个主要步骤。第一步是更改 keeper,第二步是提取代币(执行 unlock 函数)。第二步已经完全分析。关于第一步,Kevin 指出,哈希碰撞是攻击者用来调用 putCurEpochConPubKeyBytes 函数的一种巧妙技巧。然而,攻击者最初如何能够进行有效的交易来执行此调用仍然未知。

在本博客中,我们将使用来自 Ontology 的恶意交易(0xf771ba610625d5a37b67d30bf2f8829703540c86ad76542802567caaffff280c)来演示整个过程。

总而言之,我们发现:

  • Ontology 的 relayer 对来自 Ontology 链的交易缺乏足够的验证机制。
  • 只要 Poly 链上存在有效的区块,攻击者就可以直接调用 EthCrossChainData 中的 putCurEpochConPubKeyBytes 函数,而无需通过 Ethereum relayer。
  • Kevin 指出的哈希碰撞。

免责声明:本博客包含我们团队的分析结果,这些结果基于公开可用的源代码和链上交易。在没有 Poly Network 的进一步信息的情况下,我们无法验证我们的结果。

0x.1 交易与合约

攻击流程

Ontology 交易 -> Ontology relayer -> Poly 链 -> Ethereum relayer -> Ethereum

Ethereum

0x838bf9e95cb12dd76a54c9f9d2e3082eaf928270: EthCrossChainManager
0xcf2afe102057ba5c16f899271045a0a37fcb10f2: EthCrossChainData
0x250e76987d838a75310c34bf422ea9f1ac4cc906: LockProxy

交易:0xb1f70464bd95b774c6ce60fc706eb5f9e35cb5f06e6cfe7c17dcda46ffd59581

Ontology

交易:0xf771ba610625d5a37b67d30bf2f8829703540c86ad76542802567caaffff280c

Poly

交易:0x1a72a0cf65e4c08bb8aab2c20da0085d7aee3dc69369651e2e08eb798497cc80

0x2. 攻击流程

以发生在 Ethereum 上的攻击为例。 这是一次涉及三个链(及其对应的 relayer)的跨链攻击,即 Ontology Chain、Poly Chain 和 Ethereum。

整个攻击流程包含三个步骤:

  1. 攻击者首先在 Ontology Chain 上发起了一笔恶意交易(0xf771ba610625d5a37b67d30bf2f8829703540c86ad76542802567caaffff280c);
  2. 然后,攻击者修改了 Ethereum 上 EthCrossChainData 合约中存储的 keeper 的公钥;
  3. 最后,攻击者构造了一笔恶意交易来窃取加密资产。

0x2.1 第一步

攻击者首先从 Ontology 发起了一笔跨链交易(0xf771ba610625d5a37b67d30bf2f8829703540c86ad76542802567caaffff280c),其中包含一个恶意的 payload:

您可能会注意到,此 payload 包含一个经过精心设计的函数名称(以 6631 开头,转换后为 f1121318093)。这个名称是经过仔细考量的,因为攻击者将利用函数签名哈希碰撞来调用 putCurEpochConPubKeyBytes 函数(参见 Ethereum 上的 EthCrossChainData 合约)。我们在此不深入探讨哈希碰撞的细节,因为它已经被广泛讨论。

之后,这笔交易被 Ontology Chain Relayer 成功接受。请注意,不存在任何严格的验证。因此,它在 Poly Chain 上变成了一笔有效的、新的交易(0x1a72a0cf65e4c08bb8aab2c20da0085d7aee3dc69369651e2e08eb798497cc80)。

随后,这笔新交易被 Ethereum Relayer 检测到并被拒绝。因为 Ethereum Relayer 验证了目标合约地址(在此例中为 EthCrossChainData),但只允许 LockProxy

因此,处理被终止。然而,包含恶意 payload 的交易已被存储在 Poly Chain 上,可用于发起攻击。

0x2.2 第二步

攻击者通过调用 EthCrossChainManager 合约的 verifyHeaderAndExecuteTx 函数,手动向 Ethereum 发送了一笔交易。其中使用了存储在 Poly Chain 上的恶意交易数据作为输入。作为一笔有效的 Poly Chain 交易,它能够绕过 verifyHeaderAndExecuteTx 函数中的验证(包括签名和默克尔证明)。之后,EthCrossChainData 合约的 putCurEpochConPubKeyBytes 函数被调用,将原始的四个 keeper 修改为一个由攻击者控制的新 keeper(即 0xA87fB85A93Ca072Cd4e5F0D4f178Bc831Df8a00B)。

0x2.3 第三步

在 keeper 修改后,攻击者可以直接调用 verifyHeaderAndExecuteTx 函数,而无需使用 Poly Chain。最后,调用 LockProxy 合约的 unlock 函数,从 Ethereum 窃取了大量数字资产。详细分析可以在我们之前的报告中找到。

0x3. Relayer

Ontology 和 Ethereum 的 relayer 均用 Go 实现。然而,它们缺乏足够的验证,导致:

  • 攻击者可以构建一笔恶意交易,该交易将被打包进 Poly 链。
  • 攻击者可以直接调用 Ethereum 上的 EthCrossChainData 智能合约中的函数。

0x3.1 Ontology Relayer 盲目信任来自 Ontology 的跨链交易

ont_relayerhttps://github.com/polynetwork/ont-relayer)负责监听来自 Ontology 链的跨链交易并将其发送到 Poly 链。

  • Side 指的是 Ontology 链;Alliance 指的是 Poly 链。
  • CrossChainContractAddress 是 Ontology 链上的原生智能合约(编号 09)。

上图显示,Ontology relayer 启动了两个例程来监听进出 Ontology 链的跨链交易,以及一个例程来检查跨链交易的状态(第 71 行)。

在上图中,Ontology relayer 调用 Ontology 链暴露的 RPC 接口(第 215 行 GetSmartContractEventByBlock)来获取链上的事件。从第 228 行和 232 行可以看出,此例程只监听由 CrossChainContractAddress 触发的 makeFromOntProof 事件。

在上图中,在处理跨链交易时,有五项检查。前两项是检查到 Ontology 链的 RPC 请求(检查 1 和 4),三项检查参数是否为空(检查 2、3 和 5)。然而,并没有对跨链交易的语义进行任何检查,即合约和方法名称是否合理。最后,它将交易发送到 Poly 链(第 183 行)。

Ontology relayer 使用 RPC 接口(第 164 行 - SendTransaction)构建交易并发送到 Poly 链。

ProcessToAliiance**Check**AndRetry 函数仅检查交易是否失败。如果失败,它将重新发送交易。

总而言之,ont-relayer 监听 Ontology 链上由 CrossChainContractAddress 触发的所有 makeFromOntProof 事件。然后,交易将被发送到 Poly 链。请注意,来自 Ontology 链上任何人的跨链交易都会触发 makeFromOntProof 事件,从而导致其被发送到 Poly 链。

0x3.2 绕过 Ethereum Relayer

Ethereum Relayer 负责监听 Poly 链上的交易,然后将交易发送到 Ethereum。

Ethereum Relayer 启动一个 Goroutine 来监控 Poly Chain;

它监控目标是 Ethereum 的跨链交易(第 275-278 行)。然后它检查目标合约(ToContractAddress)是否是 config.TargetContracts 中配置的合约之一。如果不是,则不会将跨链交易发送到目标链(Ethereum)。

然而,攻击者可以直接与目标链交互并调用 EthCrossChainManager 中的函数。换句话说,Ethereum relayer 中的检查可以被绕过。只要恶意交易已经打包到 Poly 链上(这通过第一步的 Ontology relayer 实现),攻击者就可以直接与 EthCrossChainManager 交互。在此过程中,签名验证(ECCUtils.verifySig)和默克尔证明(ECCUtils.merkleProve)可以成功,因为 Poly 链上存在有效的交易。

通过使用前两种方法,攻击者成功地调用了 Ethereum 上的 ToContractAddress.method。结合哈希碰撞,最终调用了 putCurEpochConPubKeyBytes 函数来更改 keeper。

致谢

Yufeng Hu, Siwei Wu, Lei Wu, Yajin Zhou @ BlockSec

官方网站:https://blocksec.com/

官方 Twitter 账号:https://twitter.com/BlockSecTeam

Sign up for the latest updates