How to solve Ethernaut's Challenges
Chris
0x901c
December 14th, 2022

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.

Tips for solving the Ethernaut challenges:

  • 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 illustrate this, let’s solve Ethernaut lvl 1: Fallback

OpenZeppelin
OpenZeppelin

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.

OpenZeppelin
OpenZeppelin

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:

Tap F12 to get access to the in-browser console
Tap F12 to get access to the in-browser console

The contribution:

Tap F12 to get access to the in-browser console
Tap F12 to get access to the in-browser console

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:

Tap F12 to get access to the in-browser console
Tap F12 to get access to the in-browser console

We just contributed 0.001 ETH. We can proceed to send any non-zero amount of ether to contract.

Tap F12 to get access to in-browser console
Tap F12 to get access to in-browser console

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.

Tap F12 to get access to the in-browser console
Tap F12 to get access to the in-browser console

Thanks for reading! Follow me @le_kag7 on Twitter:

Subscribe to Chris
Receive the latest updates directly to your inbox.
Mint this entry as an NFT to add it to your collection.
Verification
This entry has been permanently stored onchain and signed by its creator.
Arweave Transaction
r8gF5nVypRIs4c_…APbzjPV0jbhv944
Author Address
0x901c6C8e5516a6d…d834Bdc5cC0e9Dc
Content Digest
6qaiqLrnOxQH8CI…zabbRK7W2cHYD-8
More from Chris
View All

Skeleton

Skeleton

Skeleton

0 Collectors