Platform: Code4rena
Start Date: 25/01/2023
Pot Size: $36,500 USDC
Total HM: 11
Participants: 173
Period: 5 days
Judge: kirk-baird
Total Solo HM: 1
Id: 208
League: ETH
Rank: 33/173
Findings: 3
Award: $140.90
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: adriro
Also found by: 0xRobocop, 0xmrhoodie, 0xngndev, AkshaySrivastav, ArmedGoose, Atarpara, Bauer, CodingNameKiki, ElKu, Garrett, HollaDieWaldfee, IllIllI, Iurii3, KIntern_NA, KmanOfficial, Lotus, M4TZ1P, MiniGlome, Ruhum, SovaSlava, bin2chen, bytes032, carrotsmuggler, cccz, chaduke, codeislight, cryptonue, doublesharp, evan, fs0c, glcanvas, gzeon, hansfriese, hihen, hl_, holme, horsefacts, ladboy233, lukris02, mahdikarimi, manikantanynala97, martin, mert_eren, mrpathfindr, omis, peakbolt, peanuts, prestoncodes, rbserver, rvierdiiev, sashik_eth, timongty, tnevler, trustindistrust, usmannk, wait, yixxas, zadaru13, zaskoh
0.7512 USDC - $0.75
The protocol allows the admin to withdraw the protocolFee after a quest's end time which is calculated based on the number of redeemed receipts times by the reward amount where the questFee is applied to this (initially 20%).
The admin can call protocolFee() several times and this value increases as more users redeem their receipt for the reward, entitling the protocol to collect more fees for hosting the quest.
The modifier onlyAdminCanWithdraw does not have any functionality stopping the function withdrawFee()
from being executed on chain multiple times. There is also no variable that keeps track of how much has been withdrawn in terms of protocol fees. This means the value returned by protocolFee() can be abused and allow for the multiple withdrawals of the protocol fee value if the quest contract's reward token balance is sufficient enough.
Provide direct links to all referenced code in GitHub. https://github.com/rabbitholegg/quest-protocol/blob/8c4c1f71221570b14a0479c216583342bd652d8d/contracts/Erc20Quest.sol#L89-L104
Potential example:
A quest which paid 100 USDC for completing an on-chain task. Max total participants of 50 After the quest end time, only 25 receipts were redeemed for a receipt so far. The contract balance is initially 6000 USDC. After receipt redeems, the balance is 3500.
If an admin called the withdrawFee() function in a transaction, protocolFee() would be calculated to equate to 500 USDC, transferring that amount to the protocolFeeRecipient. As there is no variable holding the amount of fees withdrawn, the admin could call this 6 more times draining the quest contract of any USDC as the reward token to pay the other 25 receipt holders the reward they are due.
Manual Code Review, Visual Studio
withdrawFee()
just before safeTransfer.withdrawFee()
, the amount sent to the protocolFeeReceipt should be protocolFee() - 'alreadyWithdrawn'
withdrawFee()
, withdrawRemainingTokens()
#0 - c4-judge
2023-02-05T06:00:06Z
kirk-baird marked the issue as duplicate of #23
#1 - c4-judge
2023-02-14T08:59:17Z
kirk-baird marked the issue as satisfactory
🌟 Selected for report: carrotsmuggler
Also found by: AkshaySrivastav, ElKu, HollaDieWaldfee, Iurii3, KmanOfficial, adriro, bin2chen, evan, hansfriese, hl_, mert_eren, omis, peanuts
122.948 USDC - $122.95
Where a quest has ended and not all possible receipts were minted, there will be non-claimable rewards which the quest admin/owner can withdraw from the contract. The protocolFee can be withdrawn at multiple times after the quest has ended and therefore will affect alongside receipts redeems the contracts total balance of the ERC20 reward token.
As protocolFee
returns the total overall fees that is due to protocol for this quest based on the number receipt redeems (as mentioned in my other bug finding), there is no global variable which states the amount of fees already withdrawn from the quest. If a withdrawal of fees has taken place before the quest admin/owner proceeds with a transaction to withdraw remaining tokens, the nonClaimableTokens
value will be flawed as it takes into account in the calculation tokens that have already been withdrawn as reflected by contract's current balance (IERC20(rewardToken).balanceOf(address(this))
) which would have decreased.
Provide direct links to all referenced code in GitHub. Add screenshots, logs, or any other relevant proof that illustrates the concept.
https://github.com/rabbitholegg/quest-protocol/blob/8c4c1f71221570b14a0479c216583342bd652d8d/contracts/Erc20Quest.sol#L95-L98 https://github.com/rabbitholegg/quest-protocol/blob/8c4c1f71221570b14a0479c216583342bd652d8d/contracts/Erc20Quest.sol#L81-L87
Manual Code Review, Visual Studio
withdrawFee()
just before safeTransfernonClaimableTokens
for example: uint256 unclaimedFeeTokens = protocolFee() - 'alreadyWithdrawn'; uint256 nonClaimableTokens = IERC20(rewardToken).balanceOf(address(this)) - unclaimedFeeTokens - unclaimedTokens;
#0 - c4-judge
2023-02-05T06:01:01Z
kirk-baird marked the issue as duplicate of #42
#1 - c4-judge
2023-02-06T08:24:58Z
kirk-baird marked the issue as not a duplicate
#2 - c4-judge
2023-02-06T08:25:05Z
kirk-baird marked the issue as duplicate of #61
#3 - c4-judge
2023-02-14T10:00:51Z
kirk-baird marked the issue as satisfactory
#4 - c4-judge
2023-02-23T23:48:11Z
kirk-baird changed the severity to 2 (Med Risk)