BriVault

First Flight #52
Beginner FriendlySolidity
100 EXP
View results
Submission Details
Impact: high
Likelihood: high
Invalid

Owner can arbitrarily change tournament outcome after deposits

Root + Impact

Description

  • Normally, the vault should fairly determine the winning team once the tournament ends, and payouts should be distributed according to user deposits on the correct team.

  • The current implementation allows the owner to call setWinner() at any time, even after deposits, enabling them to arbitrarily select the winner and manipulate payouts.

/// Root cause in the codebase with @> marks to highlight the relevant section
pragma solidity ^0.8.0;
contract OwnerCanSetWinner {
mapping(address => uint256) public userShares;
mapping(address => uint256) public userTeam;
uint256 public winnerTeam;
// owner can call this at any time
@> function setWinner(uint256 teamId) external /*onlyOwner*/ {
@> winnerTeam = teamId;
@> }
}

Risk

Likelihood:

  • Occurs whenever the owner calls setWinner() after deposits have been made.

  • Occurs whenever users trust the vault for fair distribution, since there is no restriction or timelock on winner selection.

Impact:

  • Impact 1: The owner can steal users’ deposits by selecting themselves or a colluding account as the winner.

  • Impact 2: Users’ trust and funds are completely compromised, making the vault effectively centralized and exploitable.



    Proof-of-Concept (PoC)

The owner has unrestricted access to setWinner(), allowing them to override fair outcome calculations even after deposits are made.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract OwnerExploitPoC {
OwnerCanSetWinner public vault;
constructor(OwnerCanSetWinner _vault) { vault = _vault; }
function cheat(uint256 teamId) external {
// Owner sets winning team after deposits
vault.setWinner(teamId);
}
}

Recommended Mitigation

Restrict setWinner() with a finalization time and prevent multiple calls, so the winner can only be set once after the deposit period ends. This enforces fairness and prevents the owner from arbitrarily changing outcomes.

- function setWinner(uint256 teamId) external /*onlyOwner*/ {
- winnerTeam = teamId;
- }
+ uint256 public winnerFinalizationTime;
+ bool public winnerSet;
+
+ function setWinner(uint256 teamId) external /*onlyOwner*/ {
+ require(block.timestamp >= winnerFinalizationTime, "cannot set before cutoff");
+ require(!winnerSet, "winner already set");
+ winnerTeam = teamId;
+ winnerSet = true;
+ }
Updates

Appeal created

bube Lead Judge 19 days ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
Assigned finding tags:

The winner is set by the owner

This is owner action and the owner is assumed to be trusted and to provide correct input arguments.

Support

FAQs

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

Give us feedback!