Closed code423n4 closed 1 year ago
https://github.com/code-423n4/2023-01-popcorn/blob/main/src/utils/MultiRewardStaking.sol#L186
An attacker can drain all the tokens from MultiRewardStaking
MultiRewardStaking
In claimtRewards important state changes are done after interactions with tokens:
claimtRewards
File: MultiRewardStaking.sol function claimRewards(address user, IERC20[] memory _rewardTokens) external accrueRewards(msg.sender, user) { for (uint8 i; i < _rewardTokens.length; i++) { uint256 rewardAmount = accruedRewards[user][_rewardTokens[i]]; if (rewardAmount == 0) revert ZeroRewards(_rewardTokens[i]); EscrowInfo memory escrowInfo = escrowInfos[_rewardTokens[i]]; if (escrowInfo.escrowPercentage > 0) { _lockToken(user, _rewardTokens[i], rewardAmount, escrowInfo); emit RewardsClaimed(user, _rewardTokens[i], rewardAmount, true); } else { _rewardTokens[i].transfer(user, rewardAmount); emit RewardsClaimed(user, _rewardTokens[i], rewardAmount, false); } accruedRewards[user][_rewardTokens[i]] = 0; } }
If an erc777 token (or other ERC20 that supports callbacks for transfers) is used as rewardToken, an attacker could use reentrancy to steal all the tokens since the rewards is zeroed after interactions.
manual audit, vs code
Follow checks, effects, interactions:
diff --git a/src/utils/MultiRewardStaking.sol b/src/utils/MultiRewardStaking.sol index 95ebefd..f8433aa 100644 --- a/src/utils/MultiRewardStaking.sol +++ b/src/utils/MultiRewardStaking.sol @@ -175,6 +175,8 @@ contract MultiRewardStaking is ERC4626Upgradeable, OwnedUpgradeable { EscrowInfo memory escrowInfo = escrowInfos[_rewardTokens[i]]; + accruedRewards[user][_rewardTokens[i]] = 0; + if (escrowInfo.escrowPercentage > 0) { _lockToken(user, _rewardTokens[i], rewardAmount, escrowInfo); emit RewardsClaimed(user, _rewardTokens[i], rewardAmount, true); @@ -182,8 +184,6 @@ contract MultiRewardStaking is ERC4626Upgradeable, OwnedUpgradeable { _rewardTokens[i].transfer(user, rewardAmount); emit RewardsClaimed(user, _rewardTokens[i], rewardAmount, false); } - - accruedRewards[user][_rewardTokens[i]] = 0; } }
or use an OpenZepplin reentrancy guard.
dmvt marked the issue as duplicate of #54
RedVeil marked the issue as sponsor confirmed
dmvt marked the issue as partial-50
dmvt marked the issue as satisfactory
Lines of code
https://github.com/code-423n4/2023-01-popcorn/blob/main/src/utils/MultiRewardStaking.sol#L186
Vulnerability details
Impact
An attacker can drain all the tokens from
MultiRewardStaking
Proof of Concept
In
claimtRewards
important state changes are done after interactions with tokens:If an erc777 token (or other ERC20 that supports callbacks for transfers) is used as rewardToken, an attacker could use reentrancy to steal all the tokens since the rewards is zeroed after interactions.
Tools Used
manual audit, vs code
Recommended Mitigation Steps
Follow checks, effects, interactions:
or use an OpenZepplin reentrancy guard.