Platform: Code4rena
Start Date: 02/08/2022
Pot Size: $50,000 USDC
Total HM: 12
Participants: 69
Period: 5 days
Judge: gzeon
Total Solo HM: 5
Id: 150
League: ETH
Rank: 5/69
Findings: 2
Award: $3,489.26
🌟 Selected for report: 1
🚀 Solo Findings: 0
3421.7489 USDC - $3,421.75
MIMOEmptyVault.sol executeAction() is supposed to pay off the debt and return the leftover assets to the owner of the Vault But In fact the emptyVault contract, after executing the executionOperation(), only pays back the flash loan, and does not transfer the leftover assets to the owner, and locked in the emptyVault contract
function executeOperation( address[] calldata assets, uint256[] calldata amounts, uint256[] calldata premiums, address initiator, bytes calldata params ) external override returns (bool) { .... .... require(flashloanRepayAmount <= vaultCollateral.balanceOf(address(this)), Errors.CANNOT_REPAY_FLASHLOAN); vaultCollateral.safeIncreaseAllowance(address(lendingPool), flashloanRepayAmount); //****Paid off the flash loan but did not transfer the remaining balance back to mimoProxy or owner ***// return true; }
Add logs to test case
test/02_integration/MIMOEmtpyVault.test.ts
it("should be able to empty vault with 1inch", async () => { ... ... ... ++++ console.log("before emptyVault balance:--->", (await wmatic.balanceOf(emptyVault.address)) + ""); const tx = await mimoProxy.execute(emptyVault.address, MIMOProxyData); const receipt = await tx.wait(1); ++++ console.log("after emptyVault balance: --->", (await wmatic.balanceOf(emptyVault.address)) + "");
print:
before emptyVault balance:---> 0 after emptyVault balance: ---> 44383268870065355782
function executeOperation( address[] calldata assets, uint256[] calldata amounts, uint256[] calldata premiums, address initiator, bytes calldata params ) external override returns (bool) { .... .... require(flashloanRepayAmount <= vaultCollateral.balanceOf(address(this)), Errors.CANNOT_REPAY_FLASHLOAN); vaultCollateral.safeIncreaseAllowance(address(lendingPool), flashloanRepayAmount); //****transfer the remaining balance back to mimoProxy or owner ***// ++++ vaultCollateral.safeTransfer(address(mimoProxy), vaultCollateral.balanceOf(address(this)) - flashloanRepayAmount); return true; }
#0 - RayXpub
2022-08-10T10:59:12Z
We confirm this is a vulnerability and intend to fix this - only the amount needed to repay the flashloan should be transferred from the MimoProxy
to the MIMOEmptyVault
action contract.
🌟 Selected for report: IllIllI
Also found by: 0x1f8b, 0xDjango, 0xNazgul, 0xc0ffEE, 8olidity, Bnke0x0, Chom, CodingNameKiki, Deivitto, Dravee, Funen, JC, JohnSmith, NoamYakov, ReyAdmirado, Rohan16, Rolezn, Sm4rty, SooYa, TomFrenchBlockchain, TomJ, Waze, __141345__, ajtra, ak1, aysha, bin2chen, bobirichman, brgltd, bulej93, c3phas, delfin454000, durianSausage, erictee, fatherOfBlocks, gogo, horsefacts, hyh, ladboy233, mics, natzuu, nxrblsrpr, oyc_109, rbserver, samruna, sikorico, simon135, tofunmi, wagmi
67.5073 USDC - $67.51
1.MIMOProxy.sol transferOwnership() Add judgment not equal address(0) to avoid loss MIMOProxy.sol transferOwnership()
function transferOwnership(address newOwner) external override { address oldOwner = owner; ++++ if (newOwner == address(0) || oldOwner == newOwner) { ++++ revert CustomErrors.INVALID_PARAMS(); ++++ } if (oldOwner != msg.sender) { revert CustomErrors.NOT_OWNER(oldOwner, msg.sender); } owner = newOwner; emit TransferOwnership(oldOwner, newOwner); }
2.setPermission() is a very important operation, recommended emit events MIMOProxy.sol setPermission() is a very important operation, recommended emit events, useful for tracking current permissions
function setPermission( address envoy, address target, bytes4 selector, bool permission ) public override { if (owner != msg.sender) { revert CustomErrors.NOT_OWNER(owner, msg.sender); } _permissions[envoy][target][selector] = permission; emit SetPermission(envoy,target,selector,permission); }