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:
- 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 theselfdestruct()
and there's no way for the receiver to prevent this because this happens on the EVM level. - 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.
- 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.
The new balance of the Ethernaut's contract can be checked on Etherscan as a result of our selfdestruct()
call:
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.