Platform: Code4rena
Start Date: 11/12/2023
Pot Size: $90,500 USDC
Total HM: 29
Participants: 127
Period: 17 days
Judge: TrungOre
Total Solo HM: 4
Id: 310
League: ETH
Rank: 113/127
Findings: 1
Award: $6.82
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: SBSecurity
Also found by: 0x_6a70, 0xanmol, 0xbepresent, 0xfave, Arz, Byteblockers, CaeraDenoir, EV_om, EllipticPoint, Infect3d, JCN, Mike_Bello90, SECURITISE, Soul22, almurhasan, c47ch3m4ll, carrotsmuggler, cccz, critical-or-high, ether_sky, evmboi32, grearlake, kaden, rbserver, smiling_heretic, whitehat-boys, zhaojie
6.8173 USDC - $6.82
A malicious user can stake for the guild token before ProfitManager#notifyPnL
and then unstake after earning the proceeds,
The malicious user did not contribute anything to the protocol, but reaped the benefits.
In ProfitManager#notifyPnL
function, distribute to the guild,
function notifyPnL( address gauge, int256 amount ) external onlyCoreRole(CoreRoles.GAUGE_PNL_NOTIFIER) { ...... // distribute to the guild if (amountForGuild != 0) { // update the gauge profit index // if the gauge has 0 weight, does not update the profit index, this is unnecessary // because the profit index is used to reattribute profit to users voting for the gauge, // and if the weigth is 0, there are no users voting for the gauge. uint256 _gaugeWeight = uint256( GuildToken(guild).getGaugeWeight(gauge) ); if (_gaugeWeight != 0) { uint256 _gaugeProfitIndex = gaugeProfitIndex[gauge]; if (_gaugeProfitIndex == 0) { _gaugeProfitIndex = 1e18; } gaugeProfitIndex[gauge] = _gaugeProfitIndex + (amountForGuild * 1e18) / _gaugeWeight; } } ..... }
claimGaugeRewards calculates gauge rewards by measuring the difference between _gaugeProfitIndex
and the user's GaugeWeight.
function claimGaugeRewards( address user, address gauge ) public returns (uint256 creditEarned) { ..... uint256 deltaIndex = _gaugeProfitIndex - _userGaugeProfitIndex; if (deltaIndex != 0) { creditEarned = (_userGaugeWeight * deltaIndex) / 1e18; userGaugeProfitIndex[user][gauge] = _gaugeProfitIndex; } if (creditEarned != 0) { emit ClaimRewards(block.timestamp, user, gauge, creditEarned); CreditToken(credit).transfer(user, creditEarned); } }
The problem, is that a malicious user can execute the stake function before ProfitManager#notifyPnL
to increase their GaugeWeight
and reap the benefits.
After receiving the revenue, the malicious user then unstake
, so that he has nothing to lose, as long as the rewards are greater than the gas fee, the malicious user can profit.
//@audit Execute before ProfitManager#notifyPnL function stake(address term, uint256 amount) external whenNotPaused { ..... //@audit incrementGauge GuildToken(guild).incrementGauge(term, guildAmount); ..... } //@audit Execute after ProfitManager#notifyPnL function unstake(address term, uint256 amount) external { // apply pending rewards //@audit got rewards (, UserStake memory userStake, bool slashed) = getRewards( msg.sender, term ); ..... }
vscode, manual
Take time into account when calculating guild rewards.
Other
#0 - c4-pre-sort
2023-12-31T13:59:11Z
0xSorryNotSorry marked the issue as sufficient quality report
#1 - c4-pre-sort
2023-12-31T13:59:43Z
0xSorryNotSorry marked the issue as duplicate of #877
#2 - c4-judge
2024-01-25T09:15:05Z
Trumpero marked the issue as not a duplicate
#3 - c4-judge
2024-01-25T09:15:14Z
Trumpero marked the issue as duplicate of #994
#4 - c4-judge
2024-01-25T09:48:02Z
Trumpero marked the issue as unsatisfactory: Invalid
#5 - c4-judge
2024-01-25T18:10:22Z
Trumpero changed the severity to 2 (Med Risk)
#6 - c4-judge
2024-01-25T18:15:30Z
Trumpero marked the issue as satisfactory