Owner Arbitrary BPF Setting in beanstalkMint Function Enables Unfair Yield Distribution
Report ID
#33096
Report type
Smart Contract
Has PoC?
Yes
Target
https://etherscan.io/address/0x39cdAf9Dc6057Fd7Ae81Aaed64D7A062aAf452fD
Impacts
- Illegitimate minting of protocol native assets
- Contract fails to deliver promised returns, but doesn't lose value
Description
The beanstalkMint function allows the contract owner to arbitrarily set the Beans Per Fertilizer (BPF) value during minting operations without verification against the current global BPF. This centralized control over a critical parameter introduces the risk of yield manipulation, potentially leading to unfair distribution of returns among users and compromising the integrity of the yield farming system.
Root Cause
The vulnerability stems from the beanstalkMint function accepting a BPF parameter directly from the owner:
function beanstalkMint(address account, uint256 id, uint128 amount, uint128 bpf) external onlyOwner {
if (_balances[id][account].amount > 0) {
uint256[] memory ids = new uint256[](1);
ids[0] = id;
_update(account, ids, bpf);
}
_balances[id][account].lastBpf = bpf;
_safeMint(
account,
id,
amount,
bytes('0')
);
}This design allows the owner to set different BPF values for different minting operations, potentially creating inconsistencies in yield accrual rates among users.
Example Calculation
Assume the current global BPF is 100:
- Owner mints 1000 tokens to themselves with BPF set to 50
- Owner mints 1000 tokens to User A with BPF set to 150
- Global BPF increases to 200 over time
- Owner's tokens accrue: (200 - 50) * 1000 = 150,000 beans
- User A's tokens accrue: (200 - 150) * 1000 = 50,000 beans
Result: Owner's tokens accrue 3x more beans than User A's, despite being minted simultaneously.
Mitigation
Modify beanstalkMint to fetch the current BPF from IBS, removing owner's ability to set arbitrary values:
Proof of concept
// Assume current global BPF is 100
fertilizer.beanstalkMint(owner, 1, 1000, 50);
fertilizer.beanstalkMint(userA, 1, 1000, 150);
// Time passes, global BPF increases to 200
// Calculate accrued beans
uint256 ownerBeans = (200 - 50) * 1000; // 150,000 beans
uint256 userABeans = (200 - 150) * 1000; // 50,000 beans
assert(ownerBeans > userABeans); // Owner unfairly accrues more beans
This PoC demonstrates how the centralized control over BPF during minting can lead to unfair yield distribution.
BIC Response
The Fertilizer contract assumes the security and correctness of implementation in the Beanstalk contract, which is the only address that is allowed to call the __update function.
Thus, we are closing this report and no reward will be issued.