The Ethernaut-Solutions repository contains the solutions using Foundry and Hardhat.
Objectives
- Make the balance of the contract greater than zero.
selfdestruct(address payable recipient)
selfdestruct
destroys the current contract, sending its funds to the given address, and ends the execution. Note that it has some caveats inherited from the EVM:
The receiving contract’s
receive
orpayable fallback
function is not executed.The contract is only really destroyed at the end of the transaction, and reverting might "undo" the destruction.
The transfer of Ether cannot be prevented.
Furthermore, all functions of the current contract are callable directly, including the current function. Even if a contract is removed by selfdestruct
, it is still part of the history of the blockchain and probably retained by most Ethereum nodes. So using selfdestruct
is not the same as deleting data from a hard disk.
Analysis
We are given the following contract:
contract Force {/*
MEOW ?
/\_/\ /
____/ o o \
/~____ =ø= /
(______)__m_m)
*/}
Don't let the cute kitty deceive you. This is an empty contract without any functions. We have to somehow send ether to the contract.
We can send ether to the contract in one of the following ways:
Directly send Ether to the contract; however, to receive Ether transfer, the contract should have either
receive
orpayable fallback
function.Receiving the rewards through coinbase transactions for mining or validating the block.
It is possible to precalculate the contract address before deployment. The attacker can send Ether to this address before the deployment of the contract, thus forcibly storing the Ether in the contract.
As discussed, smart contracts can receive Ether from other contracts using
selfdestruct
. All the Ether stored in the calling contract will then be transferred to the address specified when callingselfdestruct()
. There is no mechanism for the receiver to stop this because it takes place at the EVM level.
Exploit
Let's create a contract that will hack the level.
Go to the Remix IDE and create the following contract.
Notice the constructor is declared payable
. While deploying the above contract, send some Ether (1 wei), which will be added to the contract balance. selfdestruct
will destroy the contract and transfer Ether to instance
i.e Force.sol.
On the Ethernaut site, open the dev console and check the instance balance.
await getBalance(instance)
It will be greater than 0.
Submit the instance.
Level passed!!!😄
Key Takeaways
Don't rely on
address(this).balance
for accounting. See Self Destruct.If
selfdestruct
is implemented, authenticate that themsg.sender = owner
. Implement proper access control.If you want to deactivate your contracts, you should instead
disable
them by changing some internal state which causes all functions to revert. This makes it impossible to use the contract, as it returns Ether immediately.
The Ethernaut-Solutions repository contains the solutions using Foundry and Hardhat.
Solution using Foundry:-
Solution using Hardhat:-
More Levels