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: 80/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
Function withdrawFee
can be called by anyone and it can be repeated multiple times. Then all reward tokens can be drained easily from Quest contract to protocolFeeAddress
by attacker. If some users dit not claim their rewards before, their rewards will be drained too.
//contract Erc20Quest.sol function withdrawFee() public onlyAdminWithdrawAfterEnd { IERC20(rewardToken).safeTransfer(protocolFeeRecipient, protocolFee()); }
//contract Quest.sol modifier onlyAdminWithdrawAfterEnd() { if (block.timestamp < endTime) revert NoWithdrawDuringClaim(); _; }
Here is the test file for this issue:
https://gist.github.com/huuducsc/77e1c33a715bbcbd218edfec7ec0d991
Save this file into folder test/
and run yarn test
to run it.
It is based on the test file Erc20Quest.spec.ts
, and repeats withdrawFee
multiple times.
VS code Hardhat
Set questFee
= 0 after transfer fee
function withdrawFee() public onlyAdminWithdrawAfterEnd { IERC20(rewardToken).safeTransfer(protocolFeeRecipient, protocolFee()); questFee = 0; }
#0 - c4-judge
2023-02-05T06:01:39Z
kirk-baird marked the issue as duplicate of #23
#1 - c4-judge
2023-02-14T08:59:12Z
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
In contract Erc1155Quest
, function withdrawRemainingTokens
is used to withdraw all remaining tokens in the contract, even if these tokens are unclaimed from the minted receipts. It means after the owner calls withdrawRemainingTokens
, users can not claim any rewards from their receipts.
//contract Erc1155Quest.sol function withdrawRemainingTokens(address to_) public override onlyOwner { super.withdrawRemainingTokens(to_); IERC1155(rewardToken).safeTransferFrom( address(this), to_, rewardAmountInWeiOrTokenId, IERC1155(rewardToken).balanceOf(address(this), rewardAmountInWeiOrTokenId), '0x00' ); }
VS code
Follow the function withdrawRemainingTokens
of contract Erc20Quest
#0 - c4-judge
2023-02-05T06:03:49Z
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:15Z
This previously downgraded issue has been upgraded by kirk-baird
#3 - c4-judge
2023-02-14T09:28:02Z
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
In contract QuestFactory
, function mintReceipt
is used to mint a receipt by the hash message and signature for a quest. But because the hash message does not contain chainId
and address(this)
, the signatures made on 1 chain can be simply reused in other chains with the same messages, and other deployed contracts QuestFactory
can be reused the hash messages and signatures.
Read EIP712 for more information
/// @dev recover the signer from a hash and signature /// @param hash_ The hash of the message /// @param signature_ The signature of the hash function recoverSigner(bytes32 hash_, bytes memory signature_) public pure returns (address) { bytes32 messageDigest = keccak256(abi.encodePacked('\x19Ethereum Signed Message:\n32', hash_)); return ECDSAUpgradeable.recover(messageDigest, signature_); } /// @dev mint a RabbitHole Receipt. Note: this contract must be set as Minter on the receipt contract /// @param questId_ The id of the quest /// @param hash_ The hash of the message /// @param signature_ The signature of the hash function mintReceipt(string memory questId_, bytes32 hash_, bytes memory signature_) public { if (quests[questId_].numberMinted + 1 > quests[questId_].totalParticipants) revert OverMaxAllowedToMint(); if (quests[questId_].addressMinted[msg.sender] == true) revert AddressAlreadyMinted(); if (keccak256(abi.encodePacked(msg.sender, questId_)) != hash_) revert InvalidHash(); if (recoverSigner(hash_, signature_) != claimSignerAddress) revert AddressNotSigned(); quests[questId_].addressMinted[msg.sender] = true; quests[questId_].numberMinted++; emit ReceiptMinted(msg.sender, questId_); rabbitholeReceiptContract.mint(msg.sender, questId_); }
mintReceipt
in the contract QuestFactory
.QuestFactory
to use, Alice can reuse the above signature and message hash to call mintReceipt
for quest questId
, then claim rewards although Alice did not complete this quest.VS code
Follow the EIP712 to add the missing fields into the hash message (chainId, address(this))
#0 - c4-judge
2023-02-05T06:02:35Z
kirk-baird marked the issue as duplicate of #45
#1 - c4-judge
2023-02-14T09:36:08Z
kirk-baird changed the severity to 2 (Med Risk)
#2 - c4-judge
2023-02-14T09:36:31Z
kirk-baird marked the issue as satisfactory