ETH 강제 전송 - 2

지난 포스팅에서 selfdestruct에 대해 알아보았고, 어떤 컨트랙트에 강제로 ETH를 보내는 데 사용되는지 알아보았습니다.

이제 이 취약점이 무엇인지 이해하기 위해 예를 들어 보겠습니다. 다음 계약을 고려하십시오.

contract Crowdfund {
    // this contract only accepts a certain amount of funds
    // if someone tries to send an amount that exceeds contract balance more than the limit, tnx fails
    // funds are transfered to an address only when contract balance equals fund limit

    uint fundLimit = 3 ether;
    bool contractOpen = true;

    function donate() external payable { // for others to donate eth to this contract
        require(contractOpen, "Contract has stopped recieving funds");
        require(address(this).balance <= fundLimit, "Can't send specified amount");
        // note: we cannot do address(this).balance + msg.value, because address(this).balance already takes msg.value
    }

    function getBalance() external view returns(uint) { // to get current balance of this contract
        return address(this).balance;
    } 

    function sendFunds() external { // to send all collected funds
        require(contractOpen, "Contract has stopped recieving funds");
        require(address(this).balance == fundLimit, "Fund limit not reached yet");
        contractOpen = false; // contract closed
        payable(address(0x78731D3Ca6b7E34aC0F824c42a7cC18A495cabaB)).transfer(address(this).balance);
    }
}


이제 다음 시나리오를 고려하십시오.
  • 계약 잔액이 3ether에 도달했지만 일부 해커가 추가 ETH를 CrowdFund 계약 주소로 강제 전송합니다.
  • 이제 계약 잔액 > fundLimit이므로 require(address(this).balance == fundLimit, "Fund limit not reached yet"); 함수의 sendFunds()가 실패하고 수집된 자금을 필요한 수신자에게 보낼 수 없습니다.

  • 취약점이 정확히 무엇인지 파악하셨기를 바랍니다.

    공격자 계약을 살펴보겠습니다.

    contract Attacker{
        fallback() external payable {
    
        }
    
        function attack(address _crowdFund) external {
            selfdestruct(payable(_crowdFund));
        }
    }
    


    지금까지 우리는 이 취약점이 정확히 무엇이며 해커가 이를 악용할 수 있는 방법을 이해했습니다. 이제 그러한 해킹을 피하는 방법을 살펴보겠습니다.
  • 한 가지 해결책은 변경하는 것입니다
  • .
    require(address(this).balance == fundLimit, "Fund limit not reached yet");
    require(address(this).balance >= fundLimit, "Fund limit not reached yet"); 계약의 sendFunds() 기능에서 Crowdfund.

    그러나 그것이 일부 응용 프로그램의 중요한 조건이라면 어떨까요?
  • 더 나은 솔루션은 address(this)를 사용하여 자금
  • 을 추적하지 않는 것입니다.

    이를 더 잘 이해하기 위해 다음 계약을 살펴보겠습니다.

    contract CrowdFund_safe{
        uint fundLimit = 3 ether;
        bool contractOpen = true;
        uint balance = 0;
    
        function donate() external payable { // for others to donate eth to this contract
            require(contractOpen, "Contract has stopped recieving funds");
            require(balance + msg.value <= fundLimit, "Can't send specified amount");
            balance += msg.value;
        }
    
        function getBalance() external view returns(uint) { // to get current balance of this contract
            return address(this).balance;
        } 
    
        function sendFunds() external { // to send all collected funds
            require(contractOpen, "Contract has stopped recieving funds");
            require(balance == fundLimit, "Fund limit not reached yet");
            contractOpen = false; // contract closed
            payable(address(0x78731D3Ca6b7E34aC0F824c42a7cC18A495cabaB)).transfer(balance);
        }
    }
    


    이제 해커가 이 계약에 ETH를 강제로 보내더라도 상태 변수balance가 변경되지 않으므로 계약의 조건에 영향을 미치지 않습니다.
    따라서 계약은 그러한 공격으로부터 안전합니다.

    좋은 웹페이지 즐겨찾기