智能合约交易顺序依赖漏洞

智能合约交易顺序依赖漏洞,欢迎一起学习!

image.png 这些文章的主要目的不是教育别人,而是检验我自己的理解深度。但是,我确实希望我的努力能为您亲爱的读者提供一点价值。如果有任何问题或错误,请告诉我,以便我们一起学习和成长。

文章开头分享一个区块链安全和智能合约审计的交流群:741631068!

1. 交易顺序依赖概述

1.1 简介

交易顺序依赖 (TOD) 漏洞是一种可能存在于智能合约中的竞争条件缺陷。这些也称为前端运行漏洞。由于在以太坊区块链上处理交易的方式,可能会存在此类漏洞。

1.2 以太坊内存池

当用户创建交易时,它不会立即添加到分类帐中。交易在整个网络中广播并存储在临时保存的节点内存池中。Mempool可以被认为是未处理/待处理交易的等待区,即尚未添加到块中的交易。Mempool 是以太坊中必不可少的机制,因为一旦用户交易被添加到账本中,就无法更改。 内存池如何工作:

  1. 用户创建交易(通过 DAPP 或钱包),并发送到它连接的节点。
  2. 该节点将交易添加到自己的内存池中并执行检查以验证交易。
  3. 交易被广播到网络的其余部分,并添加到接收节点的内存池中。
  4. 交易也被挖矿节点接收,并将它们添加到块中。
  5. 成功挖出一个区块后,将其添加到区块链中,并在整个网络中广播该区块。
  6. 如果接收节点在其内存池中包含包含的交易,则会将其删除。

通常,节点为内存池指定大约 300 MB 的空间。在高网络活动期间,此存储可能会达到其限制,并且可能会删除事务以清除一些内存。为了保证一笔交易不掉线,用户可以支付更高的gas费来保证更高的优先级。更高优先级的事务也被放置在队列中更高的位置以进行处理。这可能会带来安全风险。

2. 安全风险

由于区块链的开放和去中心化特性,任何人(拥有全节点客户端)都可以跟踪交易并探索内存池以进行恶意提议。交易订单也可以通过提供更高的汽油费来操纵。矿工还能够重新安排他们开采的区块中的交易顺序。 如果关键逻辑依赖于事务的顺序,它可能会导致意外的输出和结果的颠覆。

2.1 例子

让我们看一下以下智能合约,以更好地了解该漏洞。

这是一个简单的智能合约,如果玩家猜对了数字,他们将被视为赢家。但是,该合约容易受到 TOD 的影响。攻击过程如下:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0; 

contract numGuess {
    address public winner; 

    function guess(uint _guess) external {
        require (_guess == 9841984198);
        winner = msg.sender;
    }
}
  1. 玩家将猜对数字并将其作为交易提交。
  2. 待处理的交易将存储在节点的内存池中。
  3. 作弊者将监控内存池以获取我们智能合约的交易。
  4. 作弊者将看到正确的猜测并以更高的汽油费提交。
  5. 作弊者的交易将被优先处理,并被视为赢家。 在我们的示例中,没有重大损坏。TOD 的影响取决于智能合约中实现的逻辑,通常被归类为低或中等。但是,如果存在经济损失的可能性,它可能会产生重大影响。 在我们的示例中,没有重大损坏。TOD 的影响取决于智能合约中实现的逻辑,通常被归类为低或中等。但是,如果存在经济损失的可能性,它可能会产生重大影响。 从技术上讲,该合约容易受到置换TOD 的影响。置换 TOD 漏洞不需要受害者和恶意用户之间的任何交互。TOD 漏洞的其他类型包括插入抑制

顾名思义,在插入 TOD 中,将恶意行为者的交易插入到合约和用户的交易之间。例如,如果 Alice 下单以最优价格购买 NFT,Bob 可以在 Alice 之前进行交易并购买 NFT,然后立即以更高的价格出售以获利。

最后,抑制 TOD 只是意味着用许多交易淹没合约,以延迟其他用户对功能的访问。这也称为块填充攻击。

现实世界的例子包括Bancor (BNT),这是一个在几分钟内筹集了超过 1.5 亿美元的 ICO。安全研究发现 BNT 容易受到 TOD 攻击。幸运的是,该漏洞尚未被利用,Bancor 团队声称已修复该漏洞。

3.识别漏洞

Mythril、Oyente 和 Securify 等自动化工具能够发现此类漏洞,等等。要在审核过程中手动发现 TOD,应考虑以下因素:

  1. 确定一个函数是否改变了智能合约的状态。
  2. 确定此类函数的输出是否取决于输入(交易)的顺序。

在我们的示例合同中,两个条件都是明显符合的,因此我们有漏洞。

4. 缓解措施

TOD 漏洞可能难以预防和修复,因为它们需要对代码逻辑进行重大更改。

一种解决方案是使用commit reveal hash 方案。不是直接传输答案,而是传输盐,地址和答案的哈希。这里的盐是用户选择的随机数。此哈希与用户地址一起由合同存储。现在,当用户想要提交他们的答案时,他们将创建一个包含答案和盐的交易。合约将重新计算哈希值,如果与之前的哈希值匹配,则发布答案。 对我们的示例合约进行以下修改以修复该漏洞。

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0; 

contract numGuess {
    address public winner; 
    mapping (address => bytes32) public playerHashes; 

    function storeHash(bytes32 _hash) external {
        playerHashes[msg.sender] = _hash; 
    }

    function guess(uint _answer, uint _salt) external {
        require (keccak256(abi.encodePacked(_answer, _salt, msg.sender)) == playerHashes[msg.sender]);
        require (_answer == 9841984198);
        winner = msg.sender;
    }
}

在上面,映射的代码存储与特定地址相关联的哈希。storeHash函数用于执行此操作。之后,玩家将调用guess函数并将真实答案与盐一起传入。

虽然,作弊者仍然能够拦截这些消息并将它们转发给玩家之前的合约,但这将是无用的,因为检查将在第 13 行(require (keccak256(abi.encodePacked(\_answer, \_salt, msg.sender)) == playerHashes\[msg.sender]); )失败。试图捕获答案和计算哈希以提交它们都不起作用,因为为时已晚(还可以实现基于时间的锁定机制来强制执行这一点)。

点赞 4
收藏 1
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
Polaris_tow
Polaris_tow
0x215B...F2F1
欢迎一起学习哦,web3世界的守护者! 交流群:741631068 可以加v:zz15842945138 twitter:https://twitter.com/Polaris_tow