Forgeries contest - neumo's results

A protocol for on-chain games with NFT prizes on Ethereum.

General Information

Platform: Code4rena

Start Date: 13/12/2022

Pot Size: $36,500 USDC

Total HM: 5

Participants: 77

Period: 3 days

Judge: gzeon

Total Solo HM: 1

Id: 191

League: ETH

Forgeries

Findings Distribution

Researcher Performance

Rank: 64/77

Findings: 1

Award: $19.22

🌟 Selected for report: 0

🚀 Solo Findings: 0

Awards

19.2206 USDC - $19.22

Labels

bug
3 (High Risk)
satisfactory
upgraded by judge
duplicate-146

External Links

Lines of code

https://github.com/code-423n4/2022-12-forgeries/blob/main/src/VRFNFTRandomDraw.sol#L159

Vulnerability details

Impact

The value of recoverTimelock is checked to be greater than a week and less than a year, but it should never be allowed to be shorter than block.timestamp + settings.drawBufferTime, which is the time given to the winner to claim the NFT. Otherwise it could happen that while in the winner claim period, the owner could call lastResortTimelockOwnerClaimNFT and get it first, preventing the winner from claiming it.

Proof of Concept

When initializing a new draw, function initialize does the following checks on settings.recoverTimelock and settings.drawBufferTime:

if (_settings.drawBufferTime < HOUR_IN_SECONDS) {
	revert REDRAW_TIMELOCK_NEEDS_TO_BE_MORE_THAN_AN_HOUR();
}

^- drawBufferTime must be at least 1 hour

if (_settings.drawBufferTime > MONTH_IN_SECONDS) {
	revert REDRAW_TIMELOCK_NEEDS_TO_BE_LESS_THAN_A_MONTH();
}

^- drawBufferTime must be at most 1 month

if (_settings.recoverTimelock < block.timestamp + WEEK_IN_SECONDS) {
	revert RECOVER_TIMELOCK_NEEDS_TO_BE_AT_LEAST_A_WEEK();
}

^- recoverTimelock be at least 1 week ahead in time

if (
	_settings.recoverTimelock >
	block.timestamp + (MONTH_IN_SECONDS * 12)
) {
	revert RECOVER_TIMELOCK_NEEDS_TO_BE_LESS_THAN_A_YEAR();
}

^- recoverTimelock be at most 1 year ahead in time drawBufferTime is used to set request.drawTimelock which is the time limit for the winner to withdraw the NFT. This is set when the user calls startDraw or redraw, and is set as drawBufferTime seconds in the future.

request.drawTimelock = block.timestamp + settings.drawBufferTime;

So we see that recoverTimelock is a fixed time point in the future when the owner will be available to withdraw the NFT, but it could happen that this time comes before the end of the current request.drawTimelock. And this should never be possible to ever happen because the owner should always wait until the claim period expires to be able to call lastResortTimelockOwnerClaimNFT, otherwise it would be unfair for the winner.

Tools Used

Manual analysis.

I recommend here the following fix: After this line of function _requestRoll:

request.drawTimelock = block.timestamp + settings.drawBufferTime;

Check if recoverTimelock is less than request.drawTimelock and in that case set recoverTimelock = request.drawTimelock:

if(settings.recoverTimelock < request.drawTimelock){
	settings.recoverTimelock = request.drawTimelock;
}

#0 - c4-judge

2022-12-17T15:27:44Z

gzeon-c4 marked the issue as duplicate of #146

#1 - c4-judge

2023-01-23T16:52:57Z

gzeon-c4 marked the issue as satisfactory

#2 - c4-judge

2023-01-23T17:09:21Z

gzeon-c4 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