President Elector

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

Vulnerability in Initial Presidential Selection Manipulation

Summary

After initialization, the s_currentPresidentwill be msg.senderas the initial president. The s_previousVoteEndTimeStamp is responsible for storing the start time when new president is selected. However, the initial value of s_previousVoteEndTimeStamp is 0 after constructing contract. Because of it, malicious voters can eliminate the initial president and manipulate first president who they want.

Vulnerability Details

Proof of Concepts

  1. Malicious voter calls rankCandidates function to rank their candidates.

  2. Immediately after, they call the selectPresident function to change the president before others can do so.

Proof of Code

The following testing function:

function testSkipFirstPresidentialPeriodTimeAndManipulateFirstPresident() public {
address manipulatedCandidate = candidates[0];
orderedCandidates = [manipulatedCandidate];
uint256 maliciousVoterIndex = 0;
vm.prank(voters[maliciousVoterIndex]);
rankedChoice.rankCandidates(orderedCandidates);
// `1726372910` is the timestamp at the testing moment.
vm.warp(1726372910);
rankedChoice.selectPresident();
assertEq(rankedChoice.getCurrentPresident(), manipulatedCandidate);
}

Impact

Malicious voters are able to bypass the initial president selection process by leveraging the uninitialized s_previousVoteEndTimeStamp, giving them control over the first presidential selection immediately after contract deployment.

Tools Used

Foundry

Recommendation

There are 2 practices to solve this problem.

  1. Initialize the RankedChoice::s_previousVoteEndTimeStamp with the constructive time, then must wait to select next president.

constructor(address[] memory voters) EIP712("RankedChoice", "1") {
VOTERS = voters;
i_presidentalDuration = 1460 days;
s_currentPresident = msg.sender;
s_voteNumber = 0;
+ s_previousVoteEndTimeStamp = block.timestamp;
}
  1. Allow to select president after initialization.

+ uint256 private immutable i_constructorTimeStamp;
constructor(address[] memory voters) EIP712("RankedChoice", "1") {
VOTERS = voters;
i_presidentalDuration = 1460 days;
s_currentPresident = msg.sender;
s_voteNumber = 0;
+ i_constructorTimeStamp = block.timestamp;
}
...
function selectPresident() external {
- if (block.timestamp - s_previousVoteEndTimeStamp <= i_presidentalDuration) {
- revert RankedChoice__NotTimeToVote();
- }
+ if (block.timestamp - s_previousVoteEndTimeStamp <= i_presidentalDuration || (s_previousVoteEndTimeStamp == 0 && block.timestamp - i_constructorTimeStamp <= i_presidentalDuration ) {
+ revert RankedChoice__NotTimeToVote();
+ }
...
}
Updates

Lead Judging Commences

inallhonesty Lead Judge 9 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.