Beginner FriendlyFoundry
100 EXP
View results
Submission Details
Severity: high
Invalid

Potential for Front-Running Attacks in Prediction Submissions

Summary
The makePrediction function in the ThePredicter contract is vulnerable to front-running attacks. This vulnerability allows users to potentially see and react to other players' predictions before they are confirmed on the blockchain, compromising the fairness and integrity of the prediction system.

Vulnerability Details
The vulnerable function is:

function makePrediction(
uint256 matchNumber,
ScoreBoard.Result prediction
) public payable {
if (msg.value != predictionFee) {
revert ThePredicter__IncorrectPredictionFee();
}
if (block.timestamp > START_TIME + matchNumber * 68400 - 68400) {
revert ThePredicter__PredictionsAreClosed();
}
scoreBoard.confirmPredictionPayment(msg.sender, matchNumber);
scoreBoard.setPrediction(msg.sender, matchNumber, prediction);
}

The function is public and the prediction is passed as a parameter.

Transactions are visible in the mempool before they are confirmed.

Impact

This vulnerability could lead to several negative outcomes:

  1. Unfair Advantage: Users could potentially see other players' predictions and adjust their own accordingly.

  2. Game Integrity Compromise: The prediction game loses its element of independent decision-making.

Tools Used
Manual code review

POC

function test_frontRunningVulnerability() public {
address player1 = makeAddr("player1");
address player2 = makeAddr("player2");
vm.deal(player1, 1 ether);
vm.deal(player2, 1 ether);
vm.startPrank(organizer);
vm.stopPrank();
vm.startPrank(player1);
thePredicter.register{value: 0.04 ether}();
vm.stopPrank();
vm.startPrank(player2);
thePredicter.register{value: 0.04 ether}();
vm.stopPrank();
vm.startPrank(organizer);
thePredicter.approvePlayer(player1);
thePredicter.approvePlayer(player2);
vm.stopPrank();
vm.prank(player1);
thePredicter.makePrediction{value: 0.0001 ether}(0, ScoreBoard.Result.First);
// Player2 can see Player1's transaction in the mempool and copy it
vm.prank(player2);
thePredicter.makePrediction{value: 0.0001 ether}(0, ScoreBoard.Result.First);
// Set the match result
vm.prank(organizer);
scoreBoard.setResult(0, ScoreBoard.Result.First);
// Both players should have the same score for this match
int8 player1Score = scoreBoard.getPlayerScore(player1);
int8 player2Score = scoreBoard.getPlayerScore(player2);
assertEq(player1Score, player2Score, "Front-running allowed prediction copying");
}

Recommendations

use a hash function for the prediction

Updates

Lead Judging Commences

NightHawK Lead Judge over 1 year ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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