DeFiHardhatFoundry
250,000 USDC
View results
Submission Details
Severity: high
Invalid

Missing Access Control Allows Unauthorized Setting of Internal Balances in Beanstalk L2

Summary

The ReseedInternalBalances contract lacks access control in its init function, allowing any user to call it and set arbitrary internal balances for themselves in Beanstalk L2. This vulnerability could be exploited by an attacker to manipulate the Beanstalk protocol, potentially stealing assets from the protocol.

Vulnerability Details

The init function in the ReseedInternalBalancescontract does not have any access control, which means it can be called by anyone. This allows an attacker to set arbitrary internal balances for themselves, bypassing the actual balances required in Beanstalk L1. Here are the relevant code snippets:

ReseedInternalBalances Contract

contract ReseedInternalBalances {
AppStorage internal s;
event InternalBalanceChanged(address indexed user, IERC20 indexed token, int256 delta);
struct BeanstalkInternalBalance {
address token;
address[] farmers;
uint256[] balances;
uint256 totalInternalBalance;
}
function init(
BeanstalkInternalBalance calldata beanBalances,
BeanstalkInternalBalance calldata beanEthBalances,
BeanstalkInternalBalance calldata beanWstethBalances,
BeanstalkInternalBalance calldata beanStableBalances,
BeanstalkInternalBalance calldata urBeanBalances,
BeanstalkInternalBalance calldata urBeanLpBalances
) external {
setInternalBalances(beanBalances);
setInternalBalances(beanEthBalances);
setInternalBalances(beanWstethBalances);
setInternalBalances(beanStableBalances);
setInternalBalances(urBeanBalances);
setInternalBalances(urBeanLpBalances);
}
function setInternalBalances(BeanstalkInternalBalance calldata internalBalances) internal {
uint256 totalInternalBalance;
for (uint i; i < internalBalances.farmers.length; i++) {
s.accts[internalBalances.farmers[i]].internalTokenBalance[
IERC20(internalBalances.token)
] = internalBalances.balances[i];
totalInternalBalance += internalBalances.balances[i];
emit InternalBalanceChanged(
internalBalances.farmers[i],
IERC20(internalBalances.token),
int256(internalBalances.balances[i])
);
}
require(
totalInternalBalance == internalBalances.totalInternalBalance,
"ReseedInternalBalances: totalInternalBalance mismatch"
);
s.sys.internalTokenBalanceTotal[IERC20(internalBalances.token)] = totalInternalBalance;
}
}

The init function is external and lacks any form of access control. This allows any user to call the function and pass arbitrary BeanstalkInternalBalance parameters to the setInternalBalances function. The setInternalBalances function directly sets internal balances for farmers without verifying the actual balances in Beanstalk L1, enabling an attacker to set inflated or fake balances.

Impact

An attacker could set arbitrary internal balances for themselves or other addresses in Beanstalk L2, bypassing the need to have such balances in Beanstalk L1. This could lead to significant financial losses and exploitation of the protocol by allowing attackers to withdraw or use assets they do not legitimately possess.

Tools Used

VSCode

Recommendations

Make the init function callable only by the DAO and ensure that it verifies the actual balances in Beanstalk L1 before setting internal balances in Beanstalk L2.

Updates

Lead Judging Commences

inallhonesty Lead Judge 12 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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