Platform: Code4rena
Start Date: 03/02/2022
Pot Size: $75,000 USDC
Total HM: 42
Participants: 52
Period: 7 days
Judge: GalloDaSballo
Total Solo HM: 21
Id: 83
League: ETH
Rank: 31/52
Findings: 4
Award: $231.19
๐ Selected for report: 1
๐ Solo Findings: 0
๐ Selected for report: mtz
Also found by: 0x1f8b, Czar102, GalloDaSballo, GeekyLumberjack, Randyyy, Rhynorater, Ruhum, ShadowyNoobDev, bitbopper, cccz, cmichel, csanuragjain, danb, gzeon, hickuphh3, hyh, leastwood
31.0722 USDC - $31.07
https://github.com/code-423n4/2022-02-concur/blob/main/contracts/Shelter.sol#L52
It allows a claimant to call Shelter.withdraw()
multiple times to drain the pool.
Call Shelter.withdraw()
multiple times
Add a require check require(!claimed[_token][_to], "unable to claim again");
at the beginning of the withdraw()
function to prevent a claimant from making multiple claims.
#0 - GalloDaSballo
2022-04-12T23:34:27Z
Duplicate ofย #246
๐ Selected for report: ShadowyNoobDev
Also found by: 0xw4rd3n, CertoraInc, Czar102, GalloDaSballo, Heartless, IllIllI, Jujic, Randyyy, Rhynorater, Sleepy, SolidityScan, ckksec, kirk-baird, leastwood, pauliax, peritoflores, reassor
7.97 USDC - $7.97
https://github.com/code-423n4/2022-02-concur/blob/main/contracts/ConcurRewardPool.sol#L34
Since the reward tokens are transferred before the balances are set to 0, it is possible to perform a reentrancy attack if the reward token has some kind of call back functionality e.g. ERC777. pBTC is an ERC777 token that is currently available on Convex. A similar attack occurred with imBTC on uniswap v1.
tokensToSend()
callback function through the ERC-1820 contract.tokensToSend()
function, he calls ConcurRewardPool.claimRewards()
n-1 more times to drain contract.ConcurRewardPool.claimRewards()
for the first time, the pBTC reward tokens are transferred._callTokensToSend(from, from, recipient, amount, "", "");
is called inside the transfer()
function._callTokensToSend
function definition to line 1147, you will notice that it calls IERC777Sender(implementer).tokensToSend(operator, from, to, amount, userData, operatorData);
on line 1159.tokensToSend()
function, this function will be called thus draining majority of the pBTC rewards available on the ConcurRewardPool
contract.You can also find a walkthrough replicating a similar attack here.
#0 - GalloDaSballo
2022-04-17T16:14:16Z
The warden has shown how using a specific reward token can lead to reentrnacy for the function claimRewads
Because the finding is contingent on a specific token that enables the exploit, I believe Medium Severity to be appropriate
๐ Selected for report: hickuphh3
Also found by: 0x1f8b, 0xw4rd3n, BouSalman, CertoraInc, Czar102, Dravee, IllIllI, Randyyy, Rhynorater, Ruhum, ShadowyNoobDev, Sleepy, SolidityScan, WatchPug, bitbopper, cccz, cryptphi, csanuragjain, defsec, gzeon, harleythedog, hubble, hyh, kenta, kirk-baird, leastwood, mtz, pauliax, peritoflores, rfa, robee, samruna, throttle, wuwe1, ye0lde
127.2691 USDC - $127.27
_getTotalSupply()
function name is quite misleading because it doesnโt actually get you the total supply but rather, it gets you the total number of tokens staked in the ConvexStakingWrapper
contract.d_reward
in the _calcRewardIntegral()
function by calculating the 20%, storing it in a local variable and then update d_rewards by subtracting this 20% away instead of the current d_reward = (d_reward * 4) / 5;
deposit()
function has a bad comment in the natspec. Fix should be @param _amount amount to deposit
.//no-op for cvx, crv rewards
in the addRewards()
function is deprecated and should be removed.#0 - GalloDaSballo
2022-04-27T13:51:42Z
_getTotalSupply -> Non-critical
Rounding -> Agree
Comment -> Non-critical
Comment iv -> non-critical
Valuable report that ultimately helps the dev, just has very few findings, but I do appreciate the conciseness
#1 - GalloDaSballo
2022-04-27T15:06:45Z
1+++
๐ Selected for report: WatchPug
Also found by: 0x0x0x, 0x1f8b, 0x510c, 0xNot0rious, 0xngndev, BouSalman, CertoraInc, Dravee, Heartless, IllIllI, Jujic, Randyyy, Ruhum, ShadowyNoobDev, Sleepy, SolidityScan, Tomio, bitbopper, csanuragjain, defsec, gzeon, hickuphh3, kenta, mtz, pauliax, peritoflores, rfa, robee, sabtikw, throttle, wuwe1, ye0lde
64.8796 USDC - $64.88
addRewards
function, you should store IRewardStaking(convexBooster).poolInfo(_pid)
into a locally scoped variable so it is cheaper to retrieve mainPool (line 94) and lptoken (line 98).addRewards
function, you should use extraToken
on line 131 instead of calling IRewardStaking(extraPool).rewardToken()
to retrieve it again.require(_token != address(0), "zero address");
in the add()
function is IMO not required because only the owner can call this function.require(user.amount > 0, "MasterChef: nothing to withdraw");
in the withdraw()
function is not needed because if user.amount
is actually 0, the function will still work correctly as intended since the next require statement will ensure that the user cannot withdraw more than his balance.if (_amount > 0)
in the withdraw()
function is also not needed because user.amount
is not modified when _amount
is 0.#0 - GalloDaSballo
2022-04-02T13:42:30Z
Wouldn't actually save gas as both slots are read separately
Would save 97 gas
Refreshing take but ultimately will give 0 as someone else would argue the opposite and have a point
Same as above, valid argument, no points
This saves 3 gas (pays 3 to save 6) 3
#1 - GalloDaSballo
2022-04-02T13:42:38Z
100 gas saved