RabbitHole Quest Protocol contest - Garrett's results

A protocol to distribute token rewards for completing on-chain tasks.

General Information

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

RabbitHole

Findings Distribution

Researcher Performance

Rank: 148/173

Findings: 2

Award: $3.34

🌟 Selected for report: 0

🚀 Solo Findings: 0

Lines of code

https://github.com/rabbitholegg/quest-protocol/blob/8c4c1f71221570b14a0479c216583342bd652d8d/contracts/RabbitHoleTickets.sol#L47-L50 https://github.com/rabbitholegg/quest-protocol/blob/8c4c1f71221570b14a0479c216583342bd652d8d/contracts/RabbitHoleReceipt.sol#L58-L61

Vulnerability details

The current implementation of the modifier onlyMinter() will not revert because the "require" part is missing, therefore any user will be able to access the minting functions in RabbitHoleTickets.sol and RabbitHoleReceipt.sol.

Impact

Any user than the allowed minter (minterAdress) is able to mint a receipt or ticket.

Proof of Concept

In RabbitHoleReceipt.spec.ts, on line 50, change the minterAddress with another address (for example to firstAddress):

await RHReceipt.connect(minterAddress).mint(firstAddress.address, 'def456')

With:

await RHReceipt.connect(firstAddress).mint(firstAddress.address, 'def456')

Same test in RabbitHoleTickets.spec.ts on line 38 or 46.

Tools Used

Manual check

Change to:

require(msg.sender == minterAddress, "not the allowed minter");

#0 - c4-judge

2023-02-06T23:10:40Z

kirk-baird marked the issue as duplicate of #9

#1 - c4-judge

2023-02-14T08:36:46Z

kirk-baird marked the issue as satisfactory

Lines of code

https://github.com/rabbitholegg/quest-protocol/blob/8c4c1f71221570b14a0479c216583342bd652d8d/contracts/Erc20Quest.sol#L102-L105 https://github.com/rabbitholegg/quest-protocol/blob/8c4c1f71221570b14a0479c216583342bd652d8d/contracts/Quest.sol#L76-L79

Vulnerability details

The function withdrawFee() does not account whether the fees have already been collected or not, therefore it can be called multiple times or even indefinitely, until the contract balance reaches zero. All funds will be transferred to the protocolFeeRecipient, but the participating users will not be able to get their reward. On a side note: the function use the onlyAdminWithdrawAfterEnd(), which only checks whether the endTime has been reached and there is no check on which user do the call, not sure if this is done by design. Anyway this mean that any user can call withdrawFee().

Impact

Contract can be drained (although the funds will go to protocolFeeRecipient), possibly denying the rewards or breaking the contract accounting.

Proof of Concept

In Erc20Quest.spec, copy and paste line 365 multiple times, for example:

await ethers.provider.send('evm_increaseTime', [100001]) await deployedQuestContract.withdrawFee() await deployedQuestContract.withdrawFee() await deployedQuestContract.withdrawFee()

You can see the call with go through and the contract balance will be less than expected

Tools Used

Manual check

Make withdrawFee() callable only once

#0 - c4-judge

2023-02-06T23:31:17Z

kirk-baird marked the issue as duplicate of #23

#1 - c4-judge

2023-02-14T08:54:16Z

kirk-baird marked the issue as satisfactory

#2 - c4-judge

2023-02-14T08:54:38Z

kirk-baird changed the severity to 3 (High Risk)

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax © 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter