Core Contracts

Regnum Aurum Acquisition Corp
HardhatReal World AssetsNFT
77,280 USDC
View results
Submission Details
Severity: medium
Invalid

Voting state can be manipulated

Summary

The voting system allows a proposal to succeed if the quorum is met (≥ 4% of total voting power) and forVotes > againstVotes. An attacker who accumulates at least 4% of total voting power can force quorum and manipulate the result if voter participation is low.

Vulnerability Details

https://github.com/Cyfrin/2025-02-raac/blob/main/contracts/core/governance/proposals/Governance.sol#L303

function state(uint256 proposalId) public view override returns (ProposalState) {
ProposalCore storage proposal = _proposals[proposalId];
if (proposal.startTime == 0) revert ProposalDoesNotExist(proposalId);
if (proposal.canceled) return ProposalState.Canceled;
if (proposal.executed) return ProposalState.Executed;
if (block.timestamp < proposal.startTime) return ProposalState.Pending;
if (block.timestamp < proposal.endTime) return ProposalState.Active;
// After voting period ends, check quorum and votes
ProposalVote storage proposalVote = _proposalVotes[proposalId];
uint256 currentQuorum = proposalVote.forVotes + proposalVote.againstVotes;
uint256 requiredQuorum = quorum();
// Check if quorum is met and votes are in favor
@> if (currentQuorum < requiredQuorum || proposalVote.forVotes <= proposalVote.againstVotes) {

The voting state is determined by two conditions, if both conditions are false, the proposal Succeeds.

(1) currentQuorum >= requiredQuorum -> Quorum is met

(2) proposalVote.forVotes > proposalVote.againstVotes -> More "For" votes than "Against". The votes are the users' votingPower and calculated in castVote().

https://github.com/Cyfrin/2025-02-raac/blob/main/contracts/core/governance/proposals/Governance.sol#L196-L206

uint256 weight = _veToken.getVotingPower(msg.sender);
if (weight == 0) {
revert NoVotingPower(msg.sender, block.number);
}
proposalVote.hasVoted[msg.sender] = true;
if (support) {
proposalVote.forVotes += weight;
} else {
proposalVote.againstVotes += weight;

Therefore, if the attacker accumulates 4% of total voting power to force quorum and if voter turnout is low, they can control proposal outcomes by ensuring forVotes > againstVotes.

Example Attack Scenario 1:

  1. Total Voting Power:1000

  2. Quorum Requirement (4%): 40 votes

  3. Attacker accumulates 41 votes and votes For.

  4. If no one votes against or opposition votes, the proposal Succeeds.

Example Attack Scenario 2:

  1. Total Voting Power: 1000

  2. Quorum Requirement (4%): 40 votes

  3. Attacker accumulates 41 votes and votes For.

  4. If Bob and Alice both vote against with 10

  5. Attacker's 41 votes > Bob + Alice 20 votes, the proposal Succeeds.

Impact

  1. Voting result manipulation

  2. Low Voter Participation Exploited: If most users are inactive, an attacker can control decision-making with minimal effort.

Tools Used

Manual code review

Recommended Mitigation

1 - Instead of just checking forVotes > againstVotes, consider adding a minimum percentage of forVotes relative to total votes cast where MIN_APPROVAL_PERCENTAGE could be set to something like 60%.

For example, 1 user votes (41) for and 1 user votes against (10), the approval percentage is 1 (for vote) / 2 (total votes) = 50% < MIN_APPROVAL_PERCENTAGE, proposalState.Defeated;

2 - Raise quorumNumerator from 4% to a higher value.

Updates

Lead Judging Commences

inallhonesty Lead Judge 7 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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

Give us feedback!