Skip to content

[Security] WBERAStakerVault: ERC4626 inflation/donation attack — no _decimalsOffset override #30

@sailorpepe

Description

@sailorpepe

Summary

WBERAStakerVault inherits ERC4626Upgradeable without overriding _decimalsOffset() (defaults to 0). Combined with the permissionless receiveRewards(uint256) function, this enables the classic ERC4626 share price inflation attack against first depositors.

Root Cause

  • No virtual shares: _decimalsOffset() is not overridden, so no virtual shares protection exists.
  • Permissionless donation: receiveRewards(uint256) has no access control — anyone can donate WBERA to inflate totalAssets().

Attack Path

  1. Attacker is the first depositor — deposits 1 wei WBERA → receives 1 share
  2. Attacker calls receiveRewards(1e18) → donates 1e18 WBERA directly to vault
  3. Share price = totalAssets / totalShares = (1e18 + 1) / 1 ≈ 1e18 per share
  4. Victim deposits 1.99e18 WBERA → previewDeposit = 1.99e18 * 1 / (1e18+1) = 1 share (truncated)
  5. Attacker now owns 50% of vault (1 of 2 shares) but funded ~1e18 of ~3e18 total
  6. Attacker withdraws → receives ~1.5e18, profiting ~0.5e18 at victim's expense

Existing Mitigation

The NatSpec mentions "Inflation Attack Protection: Initial deposit mechanism" — this is an operational mitigation (governance deposits first), but it is not enforced in code. If governance forgets or the vault is redeployed without the initial deposit, the attack surface exists.

Recommended Fix

Override _decimalsOffset() to add code-level virtual shares protection:

function _decimalsOffset() internal pure override returns (uint8) {
    return 3; // creates 1000 virtual shares per asset unit
}

Alternatively or additionally, add access control to receiveRewards:

function receiveRewards(uint256 amount) external onlyRole(MANAGER_ROLE) {
    WBERA.safeTransferFrom(msg.sender, address(this), amount);
    emit RewardsReceived(msg.sender, amount, totalAssets());
}

References

Severity

Medium — Exploitable only before initial governance deposit. Operational mitigation exists but is not code-enforced.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions