BriVault

First Flight #52
Beginner FriendlySolidity
100 EXP
View results
Submission Details
Severity: high
Valid

Missing Join Restrictions Enable Multiple Country Changes and Mempool Manipulation

the function permits users to overwtite their country seclection multiple times, since no restriction prevents repeated joins or reallocations of participant shares.
These flaws allow malicious users to monitor the mempool and switch to the most favorable to the most country at the last moment, manipulating reward distribution.

Description

  • When a participant joined an event his/her stakes should be locked and he/she shouldn't be able to join again.

  • A participant can repeatedly call joinEvent() to overwrite their selected country since the contract does not restrict multiple joins or validate that a user’s shares have already been allocated.

This allows strategic users to monitor the mempool and front-run other participants to change their country selection at the very last possible moment, potentially manipulating the distribution of winning rewards.

  • This also causes repeated entries in usersAddress, totalParticipantShares and userSharesToCountry

function joinEvent(uint256 countryId) public {
// Rest of the function
// @> Root Cause #1: User’s selected country can be overwritten at any time before eventStartDate.
userToCountry[msg.sender] = teams[countryId];
uint256 participantShares = balanceOf(msg.sender);
// @> Root Cause #2: Reallocation of shares to ANY country with no restriction or previous clearing.
userSharesToCountry[msg.sender][countryId] = participantShares;
// @> Root Cause #3: User can push themselves multiple times into usersAddress,
// inflating participant count and enabling repeated manipulation.
usersAddress.push(msg.sender);
numberOfParticipants++;
totalParticipantShares += participantShares;
emit joinedEvent(msg.sender, countryId);
}

Risk

Likelihood:

  • Miners or bots can detect pending participation transactions in the mempool during the block that reaches the event start time, allowing them to reorder and front-run.

Participants can repeatedly call joinEvent() before event start to modify their selected country, enabling last-second strategic behavior based on others’ choices.

Impact:

  • Attackers can manipulate the distribution of rewards by selecting the country with the highest total shares at the last possible moment.

  • Honest participants are disadvantaged because their country choice can be front-run or strategically countered, breaking fairness and predictability of the event.

Proof of Concept

  • The test shows that user firstvoted for Mexico

  • After he sees that most of the players are voting for Brazil, he switched his vote as well

function test_can_rejoin_again() public {
vm.startPrank(user1);
mockToken.approve(address(briVault), 5 ether);
vm.stopPrank();
vm.prank(user1);
briVault.deposit(1 ether, user1);
vm.prank(user1);
briVault.joinEvent(2);
string memory country = briVault.userToCountry(user1);
assertEq(country, "Mexico");
vm.prank(user1);
briVault.joinEvent(4);
country = briVault.userToCountry(user1);
assertEq(country, "Brazil");
}
// Output
Ran 1 test for test/briVault.t.sol:BriVaultTest
[PASS] test_can_rejoin_again() (gas: 391173)
Logs:
Country for which user voted 1st is : Mexico
Country for which user voted 2nd is : Brazil
Traces:
[391173] BriVaultTest::test_can_rejoin_again()
└─ ← [Stop]
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 3.36ms (322.00µs CPU time)

Recommended Mitigation

  • Introduce a mapping hasvoted that maps address to a boolean

  • Whenever a person votes check if he has voted or not and in the end turn hasVoted to true for that address

// Declaration of the mapping as the state variable
mapping(address user => bool voted_or_not) public hasVoted;
// In the joinEvent() function add
require(hasVoted[msg.sender] == false,"You have alredy voted, can't change the fact");
// In the joinEvent() function in last add
hasVoted[msg.sender] = true;
Updates

Appeal created

bube Lead Judge 21 days ago
Submission Judgement Published
Validated
Assigned finding tags:

Duplicate registration through `joinEvent`

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.

Give us feedback!