41291 sc insight winner selection vulnerability in yeetback contract allows multiple reward for the same lucky winner

#41291 [SC-Insight] Winner Selection Vulnerability in Yeetback Contract Allows Multiple Reward for the Same Lucky Winner

Submitted on Mar 13th 2025 at 12:16:19 UTC by @perseverance for Audit Comp | Yeet

  • Report ID: #41291

  • Report Type: Smart Contract

  • Report severity: Insight

  • Target: https://github.com/immunefi-team/audit-comp-yeet/blob/main/src/Yeetback.sol

  • Impacts:

    • Theft of unclaimed yield

    • Theft of unclaimed royalties

Description

Description

Brief/Intro

The Yeetback contract implements a winner selection mechanism where 10 winners are chosen from participants in each round to receive equal portions of the pot. However, due to a flaw in the winner selection algorithm, the same participant can be selected multiple times, allowing them to claim multiple rewards.

The vulnerability

Vulnerability Details

In the draftWinners function of the Yeetback contract, winners are selected using the following logic:

https://github.com/immunefi-team/audit-comp-yeet/blob/main/src/Yeetback.sol#L79-L88

    for (uint256 i; i < nrOfWinners; i++) {
            uint256 randomDataNumber = uint256(keccak256(abi.encodePacked(randomNumber, i)));
            uint256 winningYeetIndex = randomDataNumber % nrOfYeets; // index of the winning yeet
            address winnerAddress = yeetsInRound[round][winningYeetIndex];

            // Update amountToWinners and amountOfWins
            amountOfWins[round][winnerAddress] += 1;

            emit YeetbackWinner(round, winnerAddress, winnings, winningYeetIndex);
        }

The contract selects 10 winners regardless of the number of participants (nrOfYeets). Each duplicate selection increases the winner's amountOfWins count, allowing them to claim multiple portions of the reward pot.

The duplicate is not expected by the game theory and users as the document of Yeet stated: 20% of the prize pool is distributed via verifiably random lottery to 10 participating addresses . Reference: https://docs.yeetit.xyz/yeet/yeet-game/mechanics

The vulnerability exists in some scenarios:

  • Scenario 1:

    • If the nrOfYeets is less than 10, the modulo operation (randomDataNumber % nrOfYeets) will inevitably produce duplicate indices.

    Probably this scenarios is not high as the number of participants in the game usually higher than 10. But still this can happen.

  • In scenario 2 : If nrOfYeets is greateer than 10, that scenario is more likely to happen

    • There is no check to prevent the same address from being selected multiple times, so there is probability that the winningYeetIndex is duplicated multiple times. The smaller the nrOfYeets, the higher the probability of duplicates.

Impacts

About the severity assessment

This is a High severity issue because:

  1. It directly affects the economic fairness of the protocol

  2. It can lead to unfair distribution of reward when the number of participants is small

  3. The impact is more severe in early rounds or when participation is low

The duplicate is not expected by the game theory and users as the document of Yeet stated: 20% of the prize pool is distributed via verifiably random lottery to 10 participating addresses . Reference: https://docs.yeetit.xyz/yeet/yeet-game/mechanics

Bug Severity: High

I map the impact to the closest listed impact as this is related to unfair distribution of reward, lucky winner

Theft of unclaimed royalties

Theft of unclaimed yield

Recommendation

  • Take both scenarios in the algorithm

  • Implement the algorithm to avoid duplicates to avoid rewarding the same address multiple times.

Proof of Concept

Proof of Concept

Scenario 1: Small Number of Participants (nrOfYeets < 10)

  1. Alice, Bob, and Charlie participate by yeet() in round 1. Suppose the nrOfYeets is 3 participants. The contract adds them to yeetsInRound[1] array

  2. After the Round1 ends and cooldown period, a user call "restart()" in Yeet.sol to start Round 2. The contract calls addYeetback() with 100 BERA as the pot value

  3. The draftWinners() function is called back from entropyCallback() to select 10 winners

  4. Due to modulo operation with only 3 participants:

    • randomDataNumber % 3 will only produce values 0, 1, or 2

    • Some addresses must be selected multiple times to reach 10 winners

  5. Alice is selected 4 times, Bob 3 times, and Charlie 3 times

  6. When claiming rewards:

    • Alice claims 4/10 of the pot (40 BERA)

    • Bob claims 3/10 of the pot (30 BERA)

    • Charlie claims 3/10 of the pot (30 BERA)

  7. This results in unfair distribution where Alice receives more.

Scenario 2: Large Number of Participants (nrOfYeets > 10)

  1. 100 participants join Round 2 of Yeet

  2. After the Round2 ends and cooldown period, a user call "restart()" in Yeet.sol to start Round 3. The contract calls addYeetback() with 100 BERA as the pot value

  3. The draftWinners() function is called back from entropyCallback() to select 10 winners

  4. Due to the random modulo operation with 100 participants:

    • randomDataNumber % 100 can produce any value from 0 to 99

    • There is a probability that the same index is selected multiple times

  5. By chance, index 42 is selected twice:

    • First selection: randomDataNumber1 % 100 = 42

    • Second selection: randomDataNumber2 % 100 = 42

  6. The participant at index 42:

    • Gets their amountOfWins increased twice

    • Can claim 2/10 of the pot (20 BERA) instead of 1/10 (10 BERA)

  7. This creates an unfair advantage for the lucky participant who was selected multiple times

In both scenarios, the core issue is the lack of check for duplicate selections, allowing participants to receive multiple portions of the reward pot. The impact is more severe in Scenario 1 where duplicates are guaranteed, while Scenario 2 shows how the vulnerability can still affect fair distribution even with many participants.

Was this helpful?