An external contract could call the buyTickets() function and mint tickets for a draw without holding any of the rewardToken for paying for the ticket
Proof of Concept
If an attacker writes an external contract to call the buyTickets() function, tickets will be minted through each iteration of the for loop
for (uint256 i = 0; i < drawIds.length; ++i) {
ticketIds[i] = registerTicket(drawIds[i], tickets[i], frontend, referrer);
}
When registerTicket() is called, it calls the mint() function, which sends tickets to the callers address.
ticketId = mint(msg.sender, drawId, ticket);
Up to now, the contract hasn’t checked whether we have sufficient rewardToken to pay for the tickets. When execution gets to the final line of the function which takes payment, we have already received our tickets and can allow to function to fail.
Recommended Mitigation Steps
Check that the function caller has sufficient tokens to pay
Lines of code
https://github.com/code-423n4/2023-03-wenwin/blob/91b89482aaedf8b8feb73c771d11c257eed997e8/src/Lottery.sol#L110
Vulnerability details
Impact
An external contract could call the buyTickets() function and mint tickets for a draw without holding any of the rewardToken for paying for the ticket
Proof of Concept
If an attacker writes an external contract to call the
buyTickets()
function, tickets will be minted through each iteration of the for loopWhen
registerTicket()
is called, it calls themint()
function, which sends tickets to the callers address.Up to now, the contract hasn’t checked whether we have sufficient
rewardToken
to pay for the tickets. When execution gets to the final line of the function which takes payment, we have already received our tickets and can allow to function to fail.Recommended Mitigation Steps
Check that the function caller has sufficient tokens to pay