Platform: Code4rena
Start Date: 07/08/2023
Pot Size: $36,500 USDC
Total HM: 11
Participants: 125
Period: 3 days
Judge: alcueca
Total Solo HM: 4
Id: 274
League: ETH
Rank: 38/125
Findings: 2
Award: $53.89
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: thekmj
Also found by: 0xCiphky, 0xDetermination, 0xbrett8571, Eeyore, Team_Rocket, Tripathi, bart1e, deadrxsezzz, immeas, ltyu, mert_eren, pep7siup, popular00
49.6552 USDC - $49.66
https://github.com/code-423n4/2023-08-verwa/blob/main/src/GaugeController.sol#L127 https://github.com/code-423n4/2023-08-verwa/blob/main/src/GaugeController.sol#L240-L240
remove_gauge
is a method present in GaugeController.sol
which can be called only by governance. This method is prone to frontrun attack and due to that majority user's will be able to dissolve their votes for specific gauge.
All the users will call vote_for_gauge_weights
with user_weight
param zero and hence they will be able to use their votes on other gauge but weight of users who won't able to front run this tx , will be stuck in the removed gauge and no way to get them back
function remove_gauge(address _gauge) external onlyGovernance { require(isValidGauge[_gauge], "Invalid gauge address"); isValidGauge[_gauge] = false; _change_gauge_weight(_gauge, 0); emit GaugeRemoved(_gauge); }
this can be called by governance and which can be front run for the tx
function vote_for_gauge_weights(address _gauge_addr, uint256 _user_weight) external { [....] power_used = power_used + new_slope.power - old_slope.power; require(power_used >= 0 && power_used <= 10_000, "Used too much power"); [.....] }
some of all the weigh should not be more than 10000, hence they will front run initial tx to withdraw votes from the target gauge while other user's weight will be stuck here who won't able to front run the tx and their power used here will be stuck such that they won't able to retrieve it
Manual Testing
SOrry i can't suggest any mitigation steps, yet need to dig more in the protocol to finally suggest a mitigation
Access Control
#0 - c4-pre-sort
2023-08-13T05:35:57Z
141345 marked the issue as duplicate of #62
#1 - c4-judge
2023-08-25T11:10:12Z
alcueca marked the issue as partial-50
#2 - c4-judge
2023-08-25T22:43:36Z
alcueca changed the severity to 3 (High Risk)
🌟 Selected for report: RED-LOTUS-REACH
Also found by: 0x3b, 0x4non, 0xCiphky, 0xDING99YA, 0xDetermination, 0xE1, 0xG0P1, 0xStalin, 0xWaitress, 0xbrett8571, 0xhacksmithh, 0xkazim, 0xmuxyz, 0xweb3boy, 14si2o_Flint, AlexCzm, Alhakista, Bube, Bughunter101, Deekshith99, Eeyore, Giorgio, HChang26, InAllHonesty, JP_Courses, KmanOfficial, MatricksDeCoder, Mike_Bello90, MrPotatoMagic, Naubit, QiuhaoLi, RHaO-sec, Raihan, Rolezn, SUPERMAN_I4G, Shubham, Silverskrrrt, Strausses, T1MOH, Topmark, Tripathi, Watermelon, _eperezok, aakansha, auditsea, audityourcontracts, ayden, carlos__alegre, castle_chain, cducrest, ch0bu, d23e, deadrxsezzz, deth, devival, erebus, fatherOfBlocks, halden, hassan-truscova, hpsb, hunter_w3b, imkapadia, immeas, jat, kaden, kaveyjoe, klau5, koxuan, kutugu, ladboy233, lanrebayode77, leasowillow, lsaudit, markus_ether, matrix_0wl, merlin, nemveer, ni8mare, nonseodion, oakcobalt, owadez, p_crypt0, pipidu83, piyushshukla, popular00, ppetrov, rjs, sandy, sl1, supervrijdag, tay054, thekmj, wahedtalash77, windhustler, zhaojie
4.2289 USDC - $4.23
https://github.com/code-423n4/2023-08-verwa/blob/main/src/VotingEscrow.sol#L487 https://github.com/code-423n4/2023-08-verwa/blob/main/src/VotingEscrow.sol#L572
VotingEscrow’s balanceOfAt and totalSupplyAt return their corresponding values for a given block. Because the balance and supply can vary within the same block, these functions can return different values when called on the current block.
These both function use a binary search to return their values associated with the block
function _findBlockEpoch(uint256 _block, uint256 _maxEpoch) internal view returns (uint256) { // Binary search uint256 min = 0; uint256 max = _maxEpoch; // Will be always enough for 128-bit numbers for (uint256 i = 0; i < 128; i++) { if (min >= max) break; uint256 mid = (min + max + 1) / 2; if (pointHistory[mid].blk <= _block) { min = mid; } else { max = mid - 1; } } return min; }
if a block is contained in pointHistory
, the latest one will be used.
Points on the current block can be added indefinitely in pointHistory
. As a result , a user calling balanceOfAt or totalSupplyAt on the current block might not receive the latest value
Alice creates a voting contract that relies on balanceOfAt
and totalSupplyAt
. Alice creates a vote using block.number as a snapshot and corrupts the quorum percentage
Manual
these both function should not be called at current block .
change require(_blockNumber <= block.number, "Only past block number");
to require(_blockNumber < block.number, "Only past block number");
Invalid Validation
#0 - c4-pre-sort
2023-08-12T07:57:09Z
141345 marked the issue as duplicate of #74
#1 - c4-judge
2023-08-24T06:41:11Z
alcueca changed the severity to QA (Quality Assurance)
#2 - c4-judge
2023-08-24T06:41:48Z
alcueca marked the issue as grade-b
#3 - alcueca
2023-08-24T06:41:51Z
See #74