Wenwin contest - Blockian's results

The next generation of chance-based gaming.

General Information

Platform: Code4rena

Start Date: 06/03/2023

Pot Size: $36,500 USDC

Total HM: 8

Participants: 93

Period: 3 days

Judge: cccz

Total Solo HM: 3

Id: 218

League: ETH

Wenwin

Findings Distribution

Researcher Performance

Rank: 17/93

Findings: 1

Award: $407.79

🌟 Selected for report: 0

🚀 Solo Findings: 0

Findings Information

🌟 Selected for report: dingo2077

Also found by: 0x73696d616f, Blockian, d3e4, savi0ur

Labels

bug
2 (Med Risk)
downgraded by judge
satisfactory
sponsor confirmed
duplicate-343

Awards

407.7933 USDC - $407.79

External Links

Lines of code

https://github.com/code-423n4/2023-03-wenwin/blob/91b89482aaedf8b8feb73c771d11c257eed997e8/src/LotterySetup.sol#L114 https://github.com/code-423n4/2023-03-wenwin/blob/91b89482aaedf8b8feb73c771d11c257eed997e8/src/Lottery.sol#L135

Vulnerability details

Impact

A user can buy a ticket after the winning ticket was set by inserting the transaction in the same block, which allows them to win the jackpot. In addition, it allows for winning more than the intended jackpot amount.

Root-Cause

Buying tickets is blocked with the modifier beforeTicketRegistrationDeadline which reverts if block.timestamp is larger than ticketRegistrationDeadline(drawId) which is drawScheduledAt(drawId) - drawCoolDownPeriod

The draw itself reverts if block.timestamp < drawScheduledAt(currentDraw).

This means that if drawCoolDownPeriod is 0 (allowed by the constructor) and block.timestamp equals drawScheduledAt(currentDraw) a ticket can be purchased in this block, even after the drawing happens.

Result

This allows for someone to call buyTickets with currentDraw and the winning ticket. If this is done also after receiveRandomNumber is called (which in some random source implementation seems to be the case, as this is called from within executeDraw, or in the case the random number is also provided in the same block) than winAmount[drawFinalized][selectionSize] is set to be drawRewardSize(drawFinalized, selectionSize) / jackpotWinners; so that each jackpot winner receives their share from the pot. The problem is that the tickets were purchased after this calculation, causing it to be not correct and allowing for the withdrawal of more funds than in the pot.

Proof of Concept

Tools Used

Auditing

Change beforeTicketRegistrationDeadline modifier to actually check it's before the registration deadline, i.e. make it like so:

modifier beforeTicketRegistrationDeadline(uint128 drawId) { // slither-disable-next-line timestamp if (block.timestamp >= ticketRegistrationDeadline(drawId)) { revert TicketRegistrationClosed(drawId); } _; }

#0 - c4-judge

2023-03-10T10:14:52Z

thereksfour changed the severity to 2 (Med Risk)

#1 - c4-judge

2023-03-10T10:14:58Z

thereksfour marked the issue as primary issue

#2 - rand0c0des

2023-03-14T10:15:36Z

This is a good finding. I agree with medium severity because of highly unlikely scenario of deploying lottery with cool down period of 0, plus the way current oracles are working.

#3 - c4-sponsor

2023-03-14T10:15:41Z

rand0c0des marked the issue as sponsor confirmed

#4 - c4-judge

2023-03-19T09:59:05Z

thereksfour marked issue #343 as primary and marked this issue as a duplicate of 343

#5 - c4-judge

2023-03-19T10:07:31Z

thereksfour marked the issue as satisfactory

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