Skip to content

Latest commit

 

History

History
115 lines (81 loc) · 4.26 KB

File metadata and controls

115 lines (81 loc) · 4.26 KB

Decent Boysenberry Zebra

High

Potential Share Inflation Attack in AutoCompoundingPodLP

Summary

A potential vulnerability exists in the vault's share issuance and redemption mechanism due to rounding inconsistencies in AutoCompoundingPodLp.sol. An attacker can repeatedly deposit and withdraw assets to systematically reduce the total number of shares in circulation while keeping the asset count unchanged. This results in an artificial increase in the asset price per share, allowing the attacker to manipulate share value and potentially extract undue profits.

Root Cause

In the deposit function at AutoCompoundingPodLp.sol#L126, assets are converted to shares using rounding down, whereas in the withdraw function at AutoCompoundingPodLp.sol#L164, assets are converted to shares using rounding up. This inconsistency causes the share price to increase without affecting the total asset count.

Internal Pre-conditions

No response

External Pre-conditions

No response

Attack Path

  1. The vault's deposit function converts assets into shares, rounding down.
  2. The withdrawal function converts assets into shares, rounding up.
  3. An attacker can deposit assets and receive shares.
  4. When withdrawing, the attacker burns more shares than they originally received due to rounding.
  5. Repeating this process inflates the total share supply over time.

Impact

  1. The exploit reduces the total number of shares in the vault while keeping the asset count unchanged.
  2. As a result, the price per share (assets/shares) artificially increases, benefiting the attacker.

PoC

function testIncosistentRoundingInDepositAndWithdraw() {
    //set up attacker
    address attacker = makeAddr("attacker");
    deal(address(_asset), attacker, 15e18);
    vm.prank(attacker);
    _asset.approve(address(_autoCompoundingPodLp), 15e18);
   uint256 depositShares = _autoCompoundingPodLp.deposit(15e18, attacker);
   uint256 withdrawShares = _autoCompoundingPodLp.withdraw(15e18, attacker, attacker);
   // Withdraw shares will be greater than Deposit shares due to inconsistent Rounding.
   assertGT(withdrawShares, depositShares);
}

Deposit (Rounding Down)

Before Deposit

  • Total Assets: 115
  • Total Shares: 20
  • Assets to Deposit: 15

Calculation

Formula:
Math.mulDiv(depositAssets, (FACTOR * totalShares / totalAssets), FACTOR, Math.rounding.Floor);

  • Computed Shares: 2.6087
  • Final Shares (Rounded Down): 2

Withdrawal (Rounding Up)

Before Withdrawal

  • Total Assets: 130
  • Total Shares: 22
  • Assets to Withdraw: 15

Calculation

Formula:
Math.mulDiv(depositAssets, (FACTOR * totalShares / totalAssets), FACTOR, Math.rounding.Ceil);

  • Computed Shares: 3.538
  • Final Shares (Rounded Up): 4

Mitigation

Use the same rounding for deposit and withdraw either down or up.

function deposit(uint256 _assets, address _receiver) external override returns (uint256 _shares) {
        _processRewardsToPodLp(0, block.timestamp);
        _shares = _convertToShares(_assets, Math.Rounding.Floor);
        _deposit(_assets, _shares, _receiver);
    }

function withdraw(uint256 _assets, address _receiver, address _owner) external override returns (uint256 _shares) {
        _processRewardsToPodLp(0, block.timestamp);
        _shares = _convertToShares(_assets, Math.Rounding.Floor);
        _withdraw(_assets, _shares, _msgSender(), _owner, _receiver);
    }

or

function deposit(uint256 _assets, address _receiver) external override returns (uint256 _shares) {
        _processRewardsToPodLp(0, block.timestamp);
        _shares = _convertToShares(_assets, Math.Rounding.Ceil);
        _deposit(_assets, _shares, _receiver);
    }

function withdraw(uint256 _assets, address _receiver, address _owner) external override returns (uint256 _shares) {
        _processRewardsToPodLp(0, block.timestamp);
        _shares = _convertToShares(_assets, Math.Rounding.Ceil);
        _withdraw(_assets, _shares, _msgSender(), _owner, _receiver);
    }