BriVault

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

Direct Token Manipulation Enables Share Value Inflation

[C-2] Direct Token Manipulation Enables Share Value Inflation

Description

The _convertToShares function relies on the contract's actual token balance rather than tracked deposits, creating a critical vulnerability where share values can be manipulated through direct token transfers to the contract.

function _convertToShares(uint256 assets) internal view returns (uint256 shares) {
uint256 balanceOfVault = IERC20(asset()).balanceOf(address(this));
uint256 totalShares = totalSupply(); // total minted BTT shares so far
if (totalShares == 0 || balanceOfVault == 0) {
// First depositor: 1:1 ratio
// TODO: not really 1:1, it's asset - fee
return assets;
}
shares = Math.mulDiv(assets, totalShares, balanceOfVault);
}

Risk

Likelihood: HIGH

  • This vulnerability will occur whenever an attacker directly transfers tokens to the contract using the ERC20 token's transfer function, artificially inflating the vault balance used in share calculations.

  • This exploitation path requires minimal technical knowledge, uses standard ERC20 functionality available to any user, and can be executed with no special permissions or complex setup.

Impact: HIGH

While the assets parameter passed to _convertToShares is correctly calculated after deducting the fee, the function has a critical vulnerability in how it calculates shares based on balanceOfVault:

  1. The share calculation can be manipulated by directly transferring tokens to the contract to artificially inflate balanceOfVault

  2. This artificially reduces the number of shares subsequent depositors receive for the same deposit amount

  3. An attacker can exploit this to gain a larger proportion of winning payouts

Proof of Concept

The PoC demonstrates how an attacker can manipulate share pricing by directly transferring tokens to the contract:

This creates a situation where Bob receives significantly fewer shares than Alice despite making an identical deposit, dramatically reducing his potential winnings.

// First deposit - gets shares at 1:1
uint256 depositAmount = 1000 ether;
uint256 fee = 30 ether; // 3% fee
// Alice correctly gets 970 shares for 970 ether after fees
vault.deposit(depositAmount, alice);
// Attack: Inflate contract balance without minting shares
// Attacker transfers tokens directly to vault
token.transfer(address(vault), 1000 ether);
// Second deposit with manipulated balance
// The balanceOfVault is now 1970 ether, but totalShares is only 970
// Bob should get 970 shares for 1000 ether, but will get much less:
// shares = Math.mulDiv(970, 970, 1970) = 477 shares
vault.deposit(depositAmount, bob);
// Result: Bob gets only 477 shares despite depositing the same amount as Alice
// If Bob's team wins, Bob gets significantly less payout than Alice

Recommended Mitigation

The solution tracks actual deposits rather than relying on direct balance checks:

This prevents manipulation by using contract-managed accounting rather than direct token balance checks to calculate shares.

// Add a tracked total for actual deposits
uint256 private _totalDeposits;
function deposit(uint256 assets, address receiver) public override returns (uint256) {
// [existing fee calculation code]
uint256 stakeAsset = assets - fee;
// Track actual deposits rather than using contract balance
_totalDeposits += stakeAsset;
// [transfer code]
// [rest of function]
}
function _convertToShares(uint256 assets) internal view returns (uint256 shares) {
// Use tracked deposits rather than direct balance
uint256 totalAssets = _totalDeposits;
uint256 totalShares = totalSupply();
if (totalShares == 0 || totalAssets == 0) {
return assets;
}
shares = Math.mulDiv(assets, totalShares, totalAssets);
}
Updates

Appeal created

bube Lead Judge 19 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!