Problem statement
Some contracts will simply not take your money ¯_(ツ)_/¯
The goal of this level is to make the balance of the contract greater than zero.
Things that might help:
- Fallback methods
- Sometimes the best way to attack a contract is with another contract.
- See the "?" page above, section "Beyond the console"
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Force {/*
MEOW ?
/\_/\ /
____/ o o \
/~____ =ø= /
(______)__m_m)
*/}
Solution
Start with creating a new contract for the current level by clicking on the button, Get new instance. Remember to have enough eth in the connected wallet and that it's connected to the Sepolia network.
Open up the developer tool in your browser (F12) and in the console check the address of the contract.
contract.address
Open a new tab in your browser (Ctrl+t) and go to Remix.
In the file explorer, create a new file and name it HackTheForce.sol, and paste this code to the contract.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
contract HackTheForce {
function hack(address _address) public payable {
selfdestruct(payable(_address));
}
}
Compile the contract and deploy it to the Sepolia network. Make sure the you select the correct environment, Injected Provider - MetaMask
And make sure that the correct contract is selected before clicking the Deploy button. Sign the transaction with your MetaMask wallet.
Take the address from step one and paste it into the address field for the hack function.
We also need to add some Wei to send to the function. Do this by setting a value in the field above the contract.
Now click the transact button and sign the transaction, wait for the transaction to be accepted.
Now jump back to the challenge page and open up the developer tool in your browser (F12), and in the console check the balance of the contract.
await getBalance(contract.address)
If the balance is more than zero, finish up the challenge by clicking on the button, Submit instance, to commit and update the progress on the ethernaut contract.
Explanation
Clearly we need to send eth to a contract that has neither a receive or a payable fallback function.
If we try to send eth anyway the transaction will throw an exception.
However if we study the Solidity docs we can find this interesting warning.
A contract without a receive Ether function can receive Ether as a recipient of a coinbase transaction (aka miner block reward) or as a destination of a selfdestruct.
Here it seems that it's most feasible to try the selfdestruct
method of the two possible ways to send the contract some ether.
Note: The selfdestruct
got deprecated in Solidity version 0.8.18 and the reasons can be found in EIP-4758.
By using this tip, Sometimes the best way to attack a contract is with another contract, that was given in the instruction, we can create a contract that is using the selfdestruct
operation.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
contract HackTheForce {
function hack(address _address) public payable {
selfdestruct(payable(_address));
}
}
As you can see we are using an older version of solidity by using pragma solidity ^0.8.17;
because of the use of the deprecated selfdestruct
operation.
The function hack is a public payable function so that we can send some eth to transfer to the contract we are attacking.
The function body is using the selfdestruct
to send all the funds of the HackTheForce contract to the given address.
This is how the signature of the operation is described in the documentation.
selfdestruct(address payable recipient): destroy the current contract, sending its funds to the given address
The signature is forcing us to cast the address with the help of payable, selfdestruct(payable(_address));
.
With all this in place we are able to hack the contract.
The take away from the level author.