President Elector

First Flight #24
Beginner FriendlyFoundry
100 EXP
View results
Submission Details
Severity: high
Valid

Improper Vote Timing Logic Results in Immediate Presidential Election

Summary

A high-severity vulnerability was discovered in the RankedChoice smart contract, where the selectPresident function can be executed immediately after deployment. This issue allows a president to be chosen without adhering to the required election duration (i_presidentalDuration), which undermines the integrity of the election process.

Vulnerability Details

The vulnerability arises from how the s_previousVoteEndTimeStamp is initialized. This variable, used to track the end of the last vote, is set to zero upon contract deployment. In test environments or simulated blockchains, the block.timestamp starts at 0 or 1, which allows an immediate call to selectPresident without waiting for the required election duration.

However, it’s important to note that on mainnet or other live networks, the block.timestamp will reflect the actual time since the Unix epoch. This means that on mainnet, the timestamp at deployment will not be zero, but rather a large number corresponding to the time of deployment (sometime in 2024, after the audit). Nonetheless, the logic still allows the possibility of manipulating the election timing.

if (block.timestamp - s_previousVoteEndTimeStamp <= i_presidentalDuration) {
revert RankedChoice__NotTimeToVote();
}

does not prevent an immediate selection of a president after deployment, as s_previousVoteEndTimeStamp is initialized to zero, leading to an incorrect comparison.

Impact

This vulnerability allows a voter to bypass the intended election cycle and select a president immediately after contract deployment. A malicious actor could take advantage of this by electing themselves or another favored candidate right after deployment, disrupting the intended governance process and potentially centralizing control of the system.

POC

function testVoteAndSelect() public {
orderedCandidates = [candidates[0]];
vm.prank(voters[0]);
rankedChoice.rankCandidates(orderedCandidates);
console.log(
"getPreviousVoteEndTimeStam in test: ",
rankedChoice.getPreviousVoteEndTimeStam()
); // output 0
console.log("block.timestamp in test: ", block.timestamp); // output 1
// set timestamp to be accurated with mainnet:
uint256 mainnetTimeStampAtDeployment = 1726271610;
vm.warp(mainnetTimeStampAtDeployment);
// a voter chooses his favorite president (probably himself with another wallet)
assertEq(rankedChoice.getUserCurrentVote(voters[0]), orderedCandidates);
//inmediately call to select president
rankedChoice.selectPresident();
assertEq(rankedChoice.getCurrentPresident(), candidates[0]);
}
/// CONSOLE OUTPUT
[PASS] testVoteAndSelect() (gas: 689850)
Logs:
getPreviousVoteEndTimeStam in test: 0
block.timestamp in test: 1

Tools Used

Manual Analysis

  • Foundry: Smart contract testing and simulation framework

Recommendations

To mitigate this issue, two potential approaches are suggested:

  1. Implement a Logical Delay for Voting: Modify the contract to introduce a delay before the first election can take place, ensuring that no president can be selected immediately upon deployment. This could involve setting up logic that establishes a minimum time frame that must elapse before the first vote is allowed, providing a safeguard against premature elections.

  2. Set s_previousVoteEndTimeStamp to a Specific Date: Alternatively, upon deployment, set the s_previousVoteEndTimeStamp to a specific timestamp that corresponds to the election cycle of the current president. This timestamp should be aligned with an appropriate time period, allowing voters to cast their votes within a reasonable timeframe without feeling rushed. By initializing this timestamp to a realistic and fair election cycle date, the integrity of the election process is preserved.

Updates

Lead Judging Commences

inallhonesty Lead Judge 10 months ago
Submission Judgement Published
Validated
Assigned finding tags:

`s_previousVoteEndTimeStamp` variable not being initialized correctly

Support

FAQs

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