Force – Level 7 Walkthrough
Welcome to the Force level of the Ethernaut wargame by OpenZeppelin. This challenge demonstrates how ETH can be sent to a contract even if it lacks payable functions or a fallback/receive function.
Objective
The contract in this level has no payable functions and no receive/fallback function. Your goal is to force ETH into the contract’s balance and complete the level.
Getting Started
-
Select the “Force” level
-
Click “Get new instance” and approve the transaction via MetaMask
-
Once the instance is deployed, the
contract
object is available in your browser console -
Note the address of the deployed instance:
contract.address
Walkthrough
The only way to send ETH to this contract is by using the selfdestruct()
function in another contract. This opcode forces a balance transfer to any address, regardless of whether the recipient contract can receive ETH via normal means.
Step 1: Write the Exploit Contract
Create a new file in Remix IDE called ForceAttack.sol
and paste the following code:
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
contract ForceAttack {
constructor() public payable {}
function attack(address payable target) public {
selfdestruct(target);
}
}
How it works:
- The constructor is
payable
, so you can send ETH when deploying. - The
attack()
function callsselfdestruct(target)
, which destroys the contract and sends its ETH balance to the target address, even if the target contract can’t normally receive ETH.
Step 2: Deploy the Attack Contract
- In Remix, compile the contract with Solidity 0.6.0
- Deploy the contract, specifying a small amount of ETH (e.g.,
1 wei
) in the value field - Use the Injected Web3 environment (MetaMask) and ensure you’re on the correct testnet
Step 3: Execute the Attack
-
After deployment, expand your contract in Remix
-
Call the
attack()
function, passing your Ethernaut instance address as the parameter:attack("0xInstanceAddress")
-
Confirm the transaction in MetaMask
Step 4: Verify the Transfer
Check the balance of your Ethernaut contract instance:
await web3.eth.getBalance(contract.address)
The balance should now be greater than 0 (in wei).
Step 5: Submit the Level
Click Submit Instance in the Ethernaut UI and confirm in MetaMask.
🎉 Done! You’ve completed the Force level.
💡 What You Learn
- ETH can be forcibly sent to any contract using
selfdestruct
- Contracts can receive ETH even if they don’t have payable functions or a fallback/receive function
- Smart contract logic should not rely solely on balance checks for security