President Elector

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

Incorrect Candidate Input Handling in `rankCandidates` Function Leads to Potential Voting Manipulation

Incorrect Candidate Input Handling in rankCandidates Function Leads to Potential Voting Manipulation

Summary

The RankedChoice contract lacked robust handling for invalid candidate inputs in the rankCandidates function. This could have led to a range of issues, including allowing votes with invalid addresses (e.g., address(0)) or duplicated candidates, thereby compromising the integrity of the voting process. The modifications introduced ensure proper validation of candidates, preventing potential abuse or manipulation of the voting system.

Vulnerability Details

Previously, the rankCandidates function did not properly validate the candidates being ranked. Malicious or inadvertent inputs such as address(0) or duplicated candidate addresses could be submitted, which could have skewed the voting results or caused unexpected behavior in the system.

  • Invalid candidate addresses: A voter could submit address(0) as a candidate, which could corrupt the election process.

  • Duplicate candidates: A voter could list the same candidate multiple times, thereby potentially influencing the election in a biased manner.

Without proper checks, it would have been possible for a voter to manipulate their vote by ranking the same candidate multiple times, which could give that candidate an unfair advantage.

Code Analysis

Original function:

function rankCandidates(address[] memory orderedCandidates) external {
// Previous logic without thorough validation
_rankCandidates(orderedCandidates, msg.sender);
}

Key Issues:

  • No checks were made for invalid addresses (address(0)).

  • The function did not prevent duplicated candidates within the same ranked list.

  • This could have led to voting manipulation or invalid input being stored, ultimately affecting the election outcome.

Modified Function:

The function rankCandidates has been updated to include the following critical validations:

  1. Address Validity: Ensures that no candidate in the list is address(0), which would otherwise introduce invalid data into the voting process.

  2. Duplicate Prevention: Ensures that no candidate appears more than once in the voter's ranked list, preventing any bias or manipulation.

function rankCandidates(address[] memory orderedCandidates) external {
for (uint256 i = 0; i < orderedCandidates.length; i++) {
require(orderedCandidates[i] != address(0), "Invalid candidate address");
require(!_isDuplicate(orderedCandidates, orderedCandidates[i]), "Duplicate candidate");
}
_rankCandidates(orderedCandidates, msg.sender);
}
function _isDuplicate(
address[] memory candidates,
address candidate
) internal pure returns (bool) {
uint256 count = 0;
for (uint256 i = 0; i < candidates.length; i++) {
if (candidates[i] == candidate) {
count++;
if (count > 1) {
return true;
}
}
}
return false;
}

Proof of Concept (PoC)

  1. Before the fix, a voter could submit a list with the same candidate ranked multiple times or use address(0) in the list.

  2. Post-fix, these invalid inputs are rejected by the contract, ensuring proper handling of voting inputs.

Example PoC:

  1. A voter submits a list of candidates with address(0) as one of the candidates.

  2. The function now reverts with the error "Invalid candidate address", ensuring no invalid candidates are accepted.

// Step 1: Attempt to rank candidates with an invalid address
address[] memory invalidCandidates = [address(0), candidates[1], candidates[2]];
vm.expectRevert("Invalid candidate address");
rankedChoice.rankCandidates(invalidCandidates);
  1. A voter submits a list of candidates where a candidate is listed twice.

  2. The function now reverts with the error "Duplicate candidate", ensuring no duplicate candidates are accepted.

// Step 2: Attempt to rank candidates with a duplicate address
address[] memory duplicateCandidates = [candidates[0], candidates[0], candidates[2]];
vm.expectRevert("Duplicate candidate");
rankedChoice.rankCandidates(duplicateCandidates);

Impact

  • Manipulation of Votes: Without proper input validation, a malicious voter could have ranked the same candidate multiple times, unfairly boosting their chances of being elected.

  • Invalid Votes: The lack of validation for address(0) could lead to the contract storing invalid votes, potentially causing issues during the tallying process.

  • Data Integrity: By accepting duplicate or invalid addresses, the integrity of the vote and the final election result could be compromised.

Tools Used

  • Manual code review

  • Fuzz testing with randomized candidate inputs to check for invalid or duplicate entries

Recommendations

  • Input Validation: Always validate inputs in public-facing functions to prevent malicious or incorrect data from entering the contract. This includes checking for invalid addresses and ensuring the uniqueness of candidates.

  • Future Improvements: While the current validation handles the most common issues, consider extending the input validation to include other potential edge cases, such as invalid characters in candidate names (if applicable) or checking for misbehaving voters.

Test Cases

1. Test for Invalid Candidate Address

function testRankCandidatesWithInvalidAddress() public {
address[] memory invalidCandidates = [address(0), candidates[1], candidates[2]];
vm.prank(voters[0]);
vm.expectRevert("Invalid candidate address");
rankedChoice.rankCandidates(invalidCandidates);
}

2. Test for Duplicate Candidates

function testRankCandidatesWithDuplicateCandidates() public {
address[] memory duplicateCandidates = [candidates[0], candidates[0], candidates[2]];
vm.prank(voters[0]);
vm.expectRevert("Duplicate candidate");
rankedChoice.rankCandidates(duplicateCandidates);
}

Conclusion

The enhancements to the rankCandidates function strengthen the integrity of the voting process by validating candidate entries and preventing potential manipulation. These changes ensure that all candidate submissions are valid and unique, making the election process more secure and reliable.

Updates

Lead Judging Commences

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

rankCandidates() allows duplicate votes inside the `orderedCandidates` array

Appeal created

inallhonesty Lead Judge 11 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement
Assigned finding tags:

rankCandidates() allows duplicate votes inside the `orderedCandidates` array

Support

FAQs

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