Strategy in StakerVault.sol can steal more rewards even though it's designed strategies shouldn't get rewards. #85
Labels
2 (Med Risk)
Assets not at direct risk, but function/availability of the protocol could be impacted or leak value
bug
Something isn't working
disagree with severity
Sponsor confirms validity, but disagrees with warden’s risk assessment (sponsor explain in comments)
sponsor confirmed
Sponsor agrees this is a problem and intends to fix it (OK to use w/ "disagree with severity")
Lines of code
https://github.com/code-423n4/2022-05-backd/tree/main/protocol/contracts/StakerVault.sol#L95
https://github.com/code-423n4/2022-05-backd/tree/main/protocol/contracts/tokenomics/LpGauge.sol#L52-L63
Vulnerability details
Impact
Strategy in StakerVault.sol can steal more rewards even though it's designed strategies shouldn't get rewards.
Also there will be a problem with a rewarding system in LpGauge.sol so that some normal users wouldn't get rewards properly.
Proof of Concept
contracts\StakerVault.sol#L312
contracts\StakerVault.sol#L111
contracts\tokenomics\LpGauge.sol#L52
Inside LpGauge.userCheckPoint(), it's designed not to calculate LpGauge.perUserShare for strategy, but it will pass this condition because B is not a strategy.
contracts\tokenomics\LpGauge.sol#L90
Furthermore, when calculate rewards, LpGauge.poolStakedIntegral will be calculated larger than a normal user stakes same amount.
It's because StakerVault._poolTotalStaked wasn't updated when A transfers x amount to B so LpGauge.poolTotalStaked is less than correct value.
contracts\tokenomics\LpGauge.sol#L113-L117
Finally B can get more rewards than he should and the reward system will pay more rewards than it's designed.
Tools Used
Solidity Visual Developer of VSCode
Recommended Mitigation Steps
I think there will be two methods to fix.
Method 1 is to forbid a transfer between strategy and non-strategy so that strategy can't move funds to non-strategy.
Method 2 is to update StakerVault.strategiesTotalStaked and StakerVault._poolTotalStaked correctly so that strategy won't claim more rewards than he should even though he claims rewards using non-strategy.
Method 1.
You need to modify two functions. StakerVault.transfer(), StakerVault.transferFrom().
You need to add this require() at L112 for transfer().
require(strategies[msg.sender] == strategies[account], Error.FAILED_TRANSFER);
You need to add this require() at L144 for transferFrom().
require(strategies[src] == strategies[dst], Error.FAILED_TRANSFER);
Method 2.
I've explained about this method in my medium risk report "StakerVault.unstake(), StakerVault.unstakeFor() would revert with a uint underflow error of StakerVault.strategiesTotalStaked, StakerVault._poolTotalStaked"
I will copy the same code for your convenience.
You need to modify 3 functions. StakerVault.addStrategy(), StakerVault.transfer(), StakerVault.transferFrom().
You can modify addStrategy() at L98-L102 like this.
function addStrategy(address strategy) external override returns (bool) {
require(msg.sender == address(inflationManager), Error.UNAUTHORIZED_ACCESS);
require(!strategies[strategy], Error.ADDRESS_ALREADY_SET);
strategies[strategy] = true;
_poolTotalStaked -= balances[strategy];
strategiesTotalStaked += balances[strategy];
return true;
}
if(strategies[msg.sender] != strategies[account]) {
if(strategies[msg.sender]) { // from strategy to non-strategy
_poolTotalStaked += amount;
strategiesTotalStaked -= amount;
}
else { // from non-strategy to strategy
_poolTotalStaked -= amount;
strategiesTotalStaked += amount;
}
}
if(strategies[src] != strategies[dst]) {
if(strategies[src]) { // from strategy to non-strategy
_poolTotalStaked += amount;
strategiesTotalStaked -= amount;
}
else { // from non-strategy to strategy
_poolTotalStaked -= amount;
strategiesTotalStaked += amount;
}
}
The text was updated successfully, but these errors were encountered: