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: 79/173
Findings: 3
Award: $26.50
🌟 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
Any malicious user can transfer the remaining funds from any Erc20Quest contract by repeatedly calling the Erc20Quest.sol#withdrawFee function
Function Erc20Quest.sol#withdrawFee can only be called after the endTime
, but does not limit the number of times it can be called or who can call it.
Here is the function code:
/// @notice Sends the protocol fee to the protocolFeeRecipient /// @dev Only callable when the quest is ended function withdrawFee() public onlyAdminWithdrawAfterEnd { IERC20(rewardToken).safeTransfer(protocolFeeRecipient, protocolFee()); }
Manual
We can Erc20Quest.sol#withdrawFee to be called only once by adding a flag in the storage, and change the function like this:
function withdrawFee() public onlyAdminWithdrawAfterEnd { // begin add require(!withdrawFlag); withdrawFlag = true; // end add IERC20(rewardToken).safeTransfer(protocolFeeRecipient, protocolFee()); }
#0 - c4-judge
2023-02-06T08:40:25Z
kirk-baird marked the issue as duplicate of #23
#1 - c4-judge
2023-02-14T08:56:28Z
kirk-baird marked the issue as satisfactory
🌟 Selected for report: RaymondFam
Also found by: 0xMirce, AkshaySrivastav, AlexCzm, Aymen0909, BClabs, CodingNameKiki, ElKu, HollaDieWaldfee, Josiah, KIntern_NA, MiniGlome, StErMi, adriro, bin2chen, cccz, chaduke, csanuragjain, gzeon, hihen, holme, libratus, minhquanym, omis, peakbolt, peanuts, rbserver, rvierdiiev, timongty, ubermensch, usmannk, wait, zaskoh
7.046 USDC - $7.05
https://github.com/rabbitholegg/quest-protocol/blob/8c4c1f71221570b14a0479c216583342bd652d8d/contracts/Erc1155Quest.sol#L54 https://github.com/rabbitholegg/quest-protocol/blob/8c4c1f71221570b14a0479c216583342bd652d8d/contracts/Erc20Quest.sol#L81
If Erc1155Quest.sol#withdrawRemainingTokens is correct, then Erc20Quest.sol#withdrawRemainingTokens y will cause the owner to withdraw less tokens. If Erc20Quest.sol#withdrawRemainingTokens is correct, then Erc1155Quest.sol#withdrawRemainingTokens will cause the unclaimed users lose the unclaimed tokens.
Function Erc1155Quest.sol#withdrawRemainingTokens will withdraw all remaining tokens to the owner:
IERC1155(rewardToken).safeTransferFrom( address(this), to_, rewardAmountInWeiOrTokenId, IERC1155(rewardToken).balanceOf(address(this), rewardAmountInWeiOrTokenId), '0x00' );
While function Erc20Quest.sol#withdrawRemainingTokens will not withdraw the unclaimed tokens:
uint unclaimedTokens = (receiptRedeemers() - redeemedTokens) * rewardAmountInWeiOrTokenId; uint256 nonClaimableTokens = IERC20(rewardToken).balanceOf(address(this)) - protocolFee() - unclaimedTokens; IERC20(rewardToken).safeTransfer(to_, nonClaimableTokens);
Manual
We only need to change one of the two functions to make them logically consistent.
#0 - c4-judge
2023-02-06T08:57:39Z
kirk-baird marked the issue as duplicate of #42
#1 - c4-judge
2023-02-10T05:03:11Z
kirk-baird changed the severity to QA (Quality Assurance)
#2 - c4-judge
2023-02-10T05:12:14Z
This previously downgraded issue has been upgraded by kirk-baird
#3 - c4-judge
2023-02-14T09:27:52Z
kirk-baird marked the issue as satisfactory
#4 - c4-judge
2023-02-23T23:49:21Z
kirk-baird changed the severity to 2 (Med Risk)
🌟 Selected for report: AkshaySrivastav
Also found by: KIntern_NA, SovaSlava, Tointer, Tricko, V_B, __141345__, betweenETHlines, bin2chen, cccz, critical-or-high, glcanvas, halden, hihen, jesusrod15, ladboy233, libratus, m9800, minhquanym, omis, peakbolt, rbserver, romand, rvierdiiev, wait, zaskoh
18.6976 USDC - $18.70
Malicious users can call QuestFactory.sol#mintReceipt on different chains use a same signature.
The signature validated in function QuestFactory.sol#mintReceipt is obtained by signing the message hash: keccak256(abi.encodePacked(msg.sender, questId_))
.
The signing data is too simple. If a malicious user obtains a signature, it can be reused in any other chain or other contract with the same functionality, as long as the claimSignerAddress
and questId are the same.
Manual
I recommend the eip-712 to fix this issue.
#0 - c4-judge
2023-02-06T08:49:06Z
kirk-baird marked the issue as duplicate of #45
#1 - c4-judge
2023-02-14T09:35:45Z
kirk-baird marked the issue as satisfactory
#2 - c4-judge
2023-02-14T09:36:07Z
kirk-baird changed the severity to 2 (Med Risk)