Theft of all CREDIT stored in ProfitManager due to incorrect profit index initialization #374
Labels
3 (High Risk)
Assets can be stolen/lost/compromised directly
bug
Something isn't working
duplicate-1194
satisfactory
satisfies C4 submission criteria; eligible for awards
sufficient quality report
This report is of sufficient quality
Lines of code
https://github.com/code-423n4/2023-12-ethereumcreditguild/blob/main/src/governance/ProfitManager.sol#L416-L418
Vulnerability details
Description
In Ethereum Credit Guild,
ProfitManager
is the contract that receives the profits from the different terms on a market and distributes them accordingly between thesurplusBuffer
,CREDIT
holders andGUILD
voters. The part that goes toGUILD
voters is split based on the weight each voter has on the term that generated that profit.When profit is distributed to the gauge voters (
notifyPnL()
), the variablegaugeProfitIndex
is updated to represent the amount of tokens that belongs to a voter for eachGUILD
token that is used to vote on that gauge. Later, when voters want to claim the rewards they must callclaimGaugeRewards()
in order to receive their part of the profit.The issue is that the function
claimGaugeRewards()
is flawed and it allows anyone to claimCREDIT
tokens that belong to other users, effectively draining allProfitManager
contract. This is the flawed function:When a user votes for a term for the first time,
_userGaugeProfitIndex
should be updated to match_gaugeProfitIndex
in order to prevent that user claiming rewards that were distributed before the user was voting for that term. Instead, the current implementation doesn't update any index the first time the user votes because the function returns early (if (_userGaugeWeight == 0) return 0
).This early return on the function will make that later when that user calls
claimGaugeRewards()
, he'll be able to claim rewards as if he was voting for that gauge since the inception of the term. Therefore, new voters will steal rewards from early voters.Impact
Any user that starts voting for a gauge that already has generated some profits will be able to steal those profits from other users. If the user votes with enough weight on that gauge, it will be able to drain the entire
ProfitManager
contract fromCREDIT
tokens.Also, when
GUILD
becomes transferable in a future, any user will be able to just flashloan someGUILD
tokens, vote for a gauge, claim the profits, decrement all voting for that gauge and return the flashloan.Proof of Concept
The following POC can be pasted in
ProfitManager.t.sol
.The test can be run with the command:
forge test --match-test testWrongIndexes
.Tools Used
Manual Review
Recommended Mitigation Steps
In order to mitigate this issue it's recommended to set the initial
_userGaugeProfitIndex
as soon as the user increments weight on the term, this way the contract will distribute profits fairly and will prevent users from stealingCREDIT
from other users. The following is the recommended fix:Removing this early return on
claimGaugeRewards()
will make that the user profit indexes are updated as soon as the users vote for the first time in that gauge.Assessed type
Invalid Validation
The text was updated successfully, but these errors were encountered: