Ethernaut#7 - Force

Ethernaut #7 - Force

#7

Some contracts will simply not take your money. The goal of this level is to make the balance of the contract greater than zero.

Things that might help:

  • Fallback methods
  • Sometimes the best way to attack a contract is with another contract.
1
2
3
4
5
6
7
8
9
10
11
12
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Force {/*

MEOW ?
/\_/\ /
____/ o o \
/~____ =ø= /
(______)__m_m)

*/}

Contract Breakdown

Let us breakdown the contract. Mere looking at it we can see the code consists of a single contract Force compiled with solidity version ^0.8.0. And it is… completely empty. There is literally nothing here except a cute ASCII cat.

There are no functions, no receive function, no fallback function, no payable functions. Nothing. The contract does not accept ether in any traditional way.

Solution

So how do we send ether to a contract that has no way to receive it? Normally, a contract needs a receive or fallback function marked as payable to accept ether. But there is one method that bypasses all of this.

selfdestruct is a special EVM opcode that destroys a contract and forcefully sends its remaining ether balance to a designated address. The receiving contract has no say in this. It cannot reject the ether. No fallback function is called, no receive function is triggered.

So our plan is to deploy a contract, fund it with some ether, and then call selfdestruct targeting the Force contract address.

1
2
3
4
5
6
7
8
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract HackForce {
constructor(address payable _forceAddress) payable {
selfdestruct(_forceAddress);
}
}

Steps to pass:

  • Copy the HackForce contract to Remix and compile it.
  • Deploy it with the Force instance address from Ethernaut as the constructor argument.
  • Make sure to send some wei along with the deployment transaction (set a value in the “Value” field on Remix before deploying).
  • Upon deployment, the constructor executes, selfdestruct fires, the HackForce contract is destroyed, and its ether balance is forcefully sent to the Force contract.
  • The Force contract now has a balance greater than zero.

Never assume that a contract’s balance will be zero just because it has no payable functions. selfdestruct (and coinbase transactions from miners) can force ether into any contract. If your contract logic depends on address(this).balance being a specific value, it can be manipulated.