Back to homepage

Ethernaut hacking challenges: level 1 walkthrough

Published

Challenge objective

  • Get ownership of the contract
  • Withdraw the funds

You can find walkthroughs for all Ethernaut levels here

Walkthrough

Examining the contract's code we can see that there are two places in which the contract's code actually changes the owner:

  constructor() public {
    owner = msg.sender;
    contributions[msg.sender] = 1000 * (1 ether);
  }

  function contribute() public payable {
    require(msg.value < 0.001 ether);
    contributions[msg.sender] += msg.value;
    if(contributions[msg.sender] > contributions[owner]) {
      owner = msg.sender;
    }
  }

  //.......

  receive() external payable {
    require(msg.value > 0 && contributions[msg.sender] > 0);
    owner = msg.sender;
  }

In the contribute() method, if we send more ETH than the current owner, the ownership of the contract will change. The only problem is that on the constructor, the contribution of the owner is set to 1000ETH and we probably don't have that much

Notice that the receive() method on the other hand, has not being declared with the function keyword and it's payable. This means this method is a fallback function.

Fallback functions are triggered when a contract receives eth without any other data associated to the transaction, when a function identifier does not match the available functions of the smart contract.

So that's how we're going to exploit this contract. We'll have to follow these steps:

  1. Contribute with some ETH so our address is added to the contributions map
  2. Trigger the fallback function (receive()) to gain ownership of the contract
  3. Confirm the ownership of the contract has changed

Let's review this in detail.

ethernaut level 1 walkthrough

As in previous levels, we can see the available methods of the contract with contract.methods() .

To contribute we'll run contract.contribute.sendTransaction({value: 10000000000000}). This value is in wei, but as it's actually lower than 0.00 1ETH, the contract will add our wallet address to the contributions list.

To trigger the fallback function, we need to send a transaction to the contract without indicating a method. We can do this by running contract.sendTransaction({value: 10000000000000}). Notice that we do not indicate a method this time, we're just sending ETH to the contract, so this will be handled by the fall back function and change the ownership of the contract.

Once we have ownership, we can withdraw the funds by calling the withdraw() method.

Takeways

For me, the main takeways of this exercise are to learn how we can use OppenZeppelin Ownable contract to restrict the usage of some of our contract's methods to a specific user and what the fallback function is and when it's triggered.

TAGS

If you enjoyed this article consider sharing it on social media or buying me a coffee ✌️

Buy Me A Coffee