Open code423n4 opened 2 years ago
The warden has shown how, by having the same token as rewards for multiple pools, the math for claiming can be broken, allowing the depositor of one pool to claim a portion of the token reward earned by all pools.
Normally this would be contingent on implementation or overlap of the tokens, however, because we're dealing with CVX we already know for certain that CVX and cvxCRV is going to be a reward for the majority of the pools.
This finding ultimately shows how to break the accounting of the reward contract while stealing yield from all other pools, and for that reason, I believe High Severity to be valid
Lines of code
https://github.com/code-423n4/2022-02-concur/blob/shelter-client/contracts/ConvexStakingWrapper.sol#L216-L259
Vulnerability details
Impact
The
ConvexStakingWrapper.sol
implementation makes several modifications to the original design. One of the key changes is the ability to add multiple pools into the wrapper contract, where each pool is represented by a unique_pid
. By doing this, we are able to aggregate pools and their LP tokens to simplify the token distribution process.However, the interdependence between pools introduces new problems. Because the original implementation uses the contract's reward token balance to track newly claimed tokens, it is possible for a malicious user to abuse the unguarded
getReward
function to maximise the profit they are able to generate. By callinggetReward
on multiple pools with the same reward token (i.e.cvx
), users are able to siphon rewards from other pools. This inevitably leads to certain loss of rewards for users who have deposited LP tokens into these victim pools. Ascrv
andcvx
are reward tokens by default, it is very likely that someone will want to exploit this issue.Proof of Concept
Let's consider the following scenario:
_pid
0 and 1.cvx
tokens._pid
0.cvx
tokens which are to be distributed to the holders of the two pools._pid
0, the pool with_pid
1 has several stakers.IRewardStaking(convexPool[_pid]).getReward
function on both pools, resulting in 200cvx
tokens being sent to the contract._calcRewardIntegral
function on the pool with_pid
0. However, this function will calculated_reward
asbal - reward.remaining
which is effectively the change in contract balance. As we have directly claimedcvx
tokens over the two pools, thisd_reward
will be equal to 200.Altogether, this will lead to the loss of rewards for other stakers as they are unable to then claim their rewards.
https://github.com/code-423n4/2022-02-concur/blob/shelter-client/contracts/ConvexStakingWrapper.sol#L216-L259
Tools Used
Manual code review. Confirmation from Taek.
Recommended Mitigation Steps
Consider redesigning this mechanism such that all pools have their
getReward
function called in_checkpoint
. The_calcRewardIntegral
function can then ensure that each pool is allocated only a fraction of the total rewards instead of the change in contract balance. Other implementations might be more ideal, so it is important that careful consideration is taken when making these changes.