Core Contracts

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

Governance.sol - Proposal Spamming

Summary

The Governance.sol contract allows users with sufficient voting power to create proposals. However, it lacks rate-limiting mechanisms or proposal expiration conditions, making it vulnerable to proposal spamming.

Vulnerability Details

The _proposalCount variable increments indefinitely without any restriction.

function propose(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
string memory description,
ProposalType proposalType
) external override returns (uint256) {
uint256 proposerVotes = _veToken.getVotingPower(msg.sender);
if (proposerVotes < proposalThreshold) {
revert InsufficientProposerVotes(msg.sender, proposerVotes, proposalThreshold, "Below threshold");
}
if (targets.length == 0 || targets.length != values.length || targets.length != calldatas.length) {
revert InvalidProposalLength(targets.length, values.length, calldatas.length);
}
uint256 proposalId = _proposalCount++;
uint256 startTime = block.timestamp + votingDelay;
uint256 endTime = startTime + votingPeriod;
_proposals[proposalId] = ProposalCore({
id: proposalId,
proposer: msg.sender,
proposalType: proposalType,
startTime: startTime,
endTime: endTime,
executed: false,
canceled: false,
descriptionHash: keccak256(bytes(description)),
targets: targets,
values: values,
calldatas: calldatas
});
// Store the proposal data separately
_proposalData[proposalId] = ProposalData(targets, values, calldatas, description);
emit ProposalCreated(proposalId, msg.sender, targets, values, calldatas, description, proposalType, startTime, endTime, proposerVotes);
return proposalId;
}

The Governance.sol contract is vulnerable to proposal spamming because:

  1. No rate limits on proposal creation.

  2. No staking or cost for creating proposals.

  3. No cap on the number of active proposals.

This allows a malicious user to flood the governance system with proposals, blocking legitimate proposals and increasing gas costs.

Step-by-Step Attack

  1. Attacker identifies the unrestricted proposal submission vulnerability.

  2. They create an automated bot that submits proposals continuously.

  3. Governance storage fills up, preventing legitimate users from submitting proposals.

  4. Other users see increased gas costs and failed transactions.

  5. Governance system is effectively frozen, blocking security upgrades or rule changes.

  6. If governance controls emergency fund distribution, attackers could prevent fund releases.

Impact

  1. Denial-of-Service (DoS) on Governance System

    • Attackers spam proposals until the system is completely filled, preventing any legitimate proposals from being submitted.

  2. Governance Stagnation

    • Since each proposal must go through the full lifecycle, legitimate users cannot create proposals due to storage overload.

  3. High Gas Costs for Users & the Protocol

    • More proposals = higher gas usage to fetch governance data.

    • Increases execution costs for real proposal votes and processing.

  4. No Effective Governance Decisions

    • If only spam proposals exist, no meaningful governance actions can be taken.

    • Attackers can control governance flow without directly harming funds.

Proof of Concept

No limit on active proposals

describe(" Proposal Spamming Attack", function () {
this.timeout(150000); // Increase timeout to 120 seconds (2 minutes)
const MAX_SPAM_PROPOSALS = 50;
let attacker;
beforeEach(async () => {
[owner, user1, user2, attacker, ...users] = await ethers.getSigners();
// Assign enough voting power to attacker
await veToken.mock_setInitialVotingPower(attacker.address, ethers.parseEther("200000")); // 200k veRAAC
});
it("should prevent new proposals after governance is spammed", async () => {
console.log("Attacker begins spamming proposals...");
let lastProposalId;
for (let i = 0; i < MAX_SPAM_PROPOSALS; i++) {
const targets = [await testTarget.getAddress()];
const values = [0];
const calldatas = [testTarget.interface.encodeFunctionData("setValue", [42])];
const description = `Spam Proposal ${i + 1}`;
const tx = await governance.connect(attacker).propose(
targets,
values,
calldatas,
description,
0 // Proposal Type
);
const receipt = await tx.wait();
lastProposalId = receipt.logs[0].args.proposalId;
console.log(` Spam Proposal ${i + 1} submitted (ID: ${lastProposalId})`);
}
console.log(" Attacker successfully spams the governance system!");
console.log(" No more proposals can be created! Governance is frozen!");
console.log("Attempting to submit a legitimate proposal...");
const targets = [await testTarget.getAddress()];
const values = [0];
const calldatas = [testTarget.interface.encodeFunctionData("setValue", [99])];
const description = "Legitimate Proposal";
await expect(governance.connect(user1).propose(
targets,
values,
calldatas,
description,
0
)).to.be.revertedWith("Too many active proposals");
console.log(" Legitimate proposal failed: Governance is frozen due to spam attack!");
});
});

Tools Used

Manual Review

Recommendations

Implement Rate Limits – Require a cooldown period between proposals.
Enforce veRAAC Token Staking – Users should stake veRAAC to create a proposal.
Cap Active Proposals – Limit the number of simultaneous active proposals.
Add Gas Cost Enforcement – Require a minimum gas deposit for submitting proposals.

Updates

Lead Judging Commences

inallhonesty Lead Judge 7 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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

Give us feedback!