Being relatively new to the Web3 space and having a keen interest in Security has naturally led to me participating the Ethernaut Challenge hosted by OpenZeppelin. For those unfamiliar with these challenges, each challenge revolves around exploiting common attack vectors seen in the Web3 space, with a particular focus on Solidity-based smart contracts.
Since commencing these challenges, my understanding of smart contract vulnerabilities has increased exponentially. However, I am also conscious that these challenges are technically challenging and for those like me without a typical background in software engineering, further explanation of exploits are warranted. Without further ado, here is an explanation of the Ethernaut level 7 titled ‘Force’.
The Code
OpenZeppelin has provided the following smart contract to be exploited:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Force {/*
MEOW ?
/\_/\ /
____/ o o \
/~____ =ø= /
(______)__m_m)
*/}
The Challenge Description
Accompanying the code above, the following description is also provided:
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.
To simplify the above even further, the objective is to make this empty contract accept Ether, despite it having no functions.
Where to start?
Let’s breakdown the contract itself first before we move onto OpenZeppelin’s hints. The Solidity version being used is ^0.8.0 and the contract is named Force. That’s it. Importantly, there are no functions contained in this contract.
Okay now what?
First, let’s breakdown OpenZeppelin’s hints at a Fallback method.
Taken directly from Solidity by Example, `fallback` is a special function that is executed either when:
- a function that does not exist is called; or
- Ether is sent directly to a contract but `receive()` does not exist or `msg.data` is not empty.
At a very basic level, what does this mean for us?
Let’s think back to our objective, we need to force this contract to take Ether. Are we going to be able to do that by calling a function that doesn’t exist in the Force contract, from a new contract we draft? No, this does not make logical sense.
Turning to the second question, does a `receive()` function exist in the Force contract? No, which satisfies the second part of the second question. Now we must turn to whether we can we sent Ether directly to this contract considering this.
Sending Ether to a contract without a `receive` function
Whilst looking at the Solidity documentation, there are consistent warnings throughout about the utilisation of a ‘selfdestruct(address)’ function. The practical implication of this function is that it will destroy the contract that contains this function and send its balance to a contract specified in the address argument.
This is very easy to do, and results in us creating a contract that looks like this:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Boom {
constructor (address payable target) payable {
selfdestruct(target);
}
}
Remix guide
If you have the address of your Ethernaut challenge instance, be sure to copy this address into the address bar in remix, outlined here (please note this is for illustrative purposes only and your environment should be an Injected Provider):
After retrieving the address of the Force contract, it should be entered into the Boom contract’s deploy address found here:
Also note when deploying the Boom contract that the value parameter is greater than 0. This will ensure that the Force contract will have an Ether balance greater than 0, thus completing the challenge.
Concluding remarks
I hope the above has been helpful in outlining each step required when undertaking the Force Ethernaut challenge. By reading this guide you should be able to understand:
- how a `fallback` function operates;
- the implications of using a `selfdestruct` function in Solidity; and
- how to practically deploy a contract using Remix.
Ready for the next Ethernaut challenge? Click to check out the previous ethernaut challenge and see what’s next in our series!
FAQs
What is Ethernaut Level 7: Force challenge about?
- The challenge involves figuring out how to force Ether into a contract that doesn’t have a payable fallback function.
How do you force ETH into a smart contract in Ethereum?
- Use techniques like self-destruct from another contract that sends its balance, even if the recipient contract lacks payable functions. Please note, this involves extreme risk and users should proceed with caution when using self-destruct.
What skills are essential to solve the Ethernaut challenges?
- Understanding of Solidity, familiarity with Ethereum smart contract mechanics and, problem-solving skills in a blockchain context.
Are there specific tools recommended for Ethernaut challenges?
- Tools like Remix IDE for Solidity, MetaMask for interacting with Ethereum, and Etherscan for blockchain insights are recommended.
Can I participate in Ethernaut challenges without any ETH?
- Yes, you can use test networks like Sepolia or Holesky where test ETH is freely available and sufficient for learning and challenges.