Ethernaut challenges are a series of online puzzles that test a user’s knowledge of the Ethereum blockchain and smart contract programming. Each challenge presents a unique scenario and requires the user to find and exploit a vulnerability in a smart contract in order to “hack” the contract and complete the challenge.
Solving the challenges can be a fun and engaging way to improve your understanding of the EVM and smart contract development. It can also be a valuable learning experience, as these challenges are designed to simulate real-world vulnerabilities and exploits that could occur.
Start with the basics: Before diving into the more advanced challenges, it’s important to familiarize yourself with basics of Solidity. This will give you a solid foundation to build on as you progress through the challenges.
Read the instructions carefully: Each challenge has a set of instructions that describe the scenario and the goals you need to achieve. Be sure to read these instructions carefully and understand what you need to do before attempting to solve the challenge.
Test the contract: Before trying to hack the contract, it’s important to test it to see how it behaves under normal conditions. This will give you an idea of what the contract is supposed to do, and it will also help you identify any potential vulnerabilities.
Use a debugger and/or your browser console: Many Ethernaut challenges require you to find and exploit a vulnerability in the contract’s code. A debugger is a useful tool that allows you to step through the contract’s code line by line, which can help you identify any potential vulnerabilities.
Think outside the box: The challenges are designed to be difficult, and they often require you to think creatively in order to find a solution. Don’t be afraid to try new approaches or to experiment with different strategies.
Ask for help: If you get stuck on a challenge, don’t be afraid to ask for help. There are many ways to receive advice and guidance from other experienced developers.
To solve this challenge, you will need to figure out a way to change the contract’s fallback function so that it does not revert when you try to send ether to it. But first, let’s read the instructions carefully.
Now that we’ve read the instructions, let’s look at the contract’s code and try to understand what it does. The fallback function is the function that is called when someone tries to send Ether to the contract without specifying which function to call. In this case, the fallback function simply reverts the transaction, which means it undoes the transaction and sends the ether back to the sender. Before that, click on Get new instance.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Fallback {
mapping(address => uint) public contributions;
address public owner;
constructor() {
owner = msg.sender;
contributions[msg.sender] = 1000 * (1 ether);
}
modifier onlyOwner {
require(
msg.sender == owner,
"caller is not the owner"
);
_;
}
function contribute() public payable {
require(msg.value < 0.001 ether);
contributions[msg.sender] += msg.value;
if(contributions[msg.sender] > contributions[owner]) {
owner = msg.sender;
}
}
function getContribution() public view returns (uint) {
return contributions[msg.sender];
}
function withdraw() public onlyOwner {
payable(owner).transfer(address(this).balance);
}
receive() external payable {
require(msg.value > 0 && contributions[msg.sender] > 0);
owner = msg.sender;
}
}
To solve the challenge, you need to figure out a way to change the fallback function so that it does not revert the transaction. There are ways to call a fallback function:
Sending Ether without any data to the contract
Calling a function that doesn’t exist inside the contract
Calling a function without passing in required data
Moreover, it has been stated that we (player
) need to be the owner
of the contract. Going through the contract ABI, methods and web3.js
let’s now check the which address is the owner
and its contribution:
The contribution:
That’s 1000 ETH when converted. You can try to drain all the faucets around but I doubt you can do it in a short amount of time. Although, if you take a closer look at the receive
fallback function you’ll notice that: If the contract has received a non-zero contribution from player
and then sends a non-zero amount of ETH to the contract, they can claim ownership.
So let’s contribute:
We just contributed 0.001 ETH. We can proceed to send any non-zero amount of ether to contract.
player
is now the contract owner. You can verify it by running the following:
await contract.owner()
You should have your address as the output.
Now submit the level instance to conclude. You should be able to see something like this in your console.