Forgeries contest - csanuragjain'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: 59/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/0xigami/vrf-nft-raffle/blob/main/src/VRFNFTRandomDraw.sol#L304

Vulnerability details

Impact

It was observed that if a winner is chosen and request.drawTimelock is still remaining, still Admin can withdraw the NFT. This will cause winner to get nothing

Proof of Concept

  1. Admin has started new draw using startDraw function. Lets say settings.recoverTimelock is X
function startDraw() external onlyOwner returns (uint256) { // Only can be called on first drawing if (request.currentChainlinkRequestId != 0) { revert REQUEST_IN_FLIGHT(); } // Emit setup draw user event emit SetupDraw(msg.sender, settings); // Request initial roll _requestRoll(); // Attempt to transfer token into this address try IERC721EnumerableUpgradeable(settings.token).transferFrom( msg.sender, address(this), settings.tokenId ) {} catch { revert TOKEN_NEEDS_TO_BE_APPROVED_TO_CONTRACT(); } // Return the current chainlink request id return request.currentChainlinkRequestId; }
  1. A winner is chosen by chainlink using the fulfillRandomWords function

  2. Assume User A has won

  3. settings.recoverTimelock (X) time has passed, but request.drawTimelock is still left

  4. Admin simply calls lastResortTimelockOwnerClaimNFT function which transfer the NFT back to Admin even though request.drawTimelock is not reached and winner was already present

function lastResortTimelockOwnerClaimNFT() external onlyOwner { // If recoverTimelock is not setup, or if not yet occurred if (settings.recoverTimelock > block.timestamp) { // Stop the withdraw revert RECOVERY_IS_NOT_YET_POSSIBLE(); } // Send event for indexing that the owner reclaimed the NFT emit OwnerReclaimedNFT(owner()); // Transfer token to the admin/owner. IERC721EnumerableUpgradeable(settings.token).transferFrom( address(this), owner(), settings.tokenId ); }

Admin should not be allowed to withdraw if request.drawTimelock is not over.

function lastResortTimelockOwnerClaimNFT() external onlyOwner { require(request.drawTimelock < block.timestamp && request.hasChosenRandomNumber, "Cannot withdraw now"); ... }

#0 - c4-judge

2022-12-17T16:41:25Z

gzeon-c4 marked the issue as duplicate of #146

#1 - c4-judge

2023-01-23T16:46:19Z

gzeon-c4 marked the issue as satisfactory

#2 - c4-judge

2023-01-23T17:09:45Z

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