Core Contracts

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

Lack of randomness in proposal salt can result in hash collision

Summary

The ID of a proposal is generated by hashing its parameters, with the hash of the description used as a salt. However, if a recurring proposal needs to be executed at regular intervals with identical parameters, reusing the same description will result in the same proposal ID. As users vote on the proposal, reaching quorum will alter its state. Consequently, when the proposal is resubmitted, it will no longer be executable since its associated operation ID will no longer be active.

Vulnerability Details

Impact

Paste this into the governance tests:

it("should execute successful proposal", async () => {
// Setup voting power
await veToken.mock_setVotingPower(await user1.getAddress(), ethers.parseEther("6000000"));
const startTime = await moveToNextTimeframe();
expect(await governance.state(proposalId)).to.equal(ProposalState.Active);
// Cast vote
await governance.connect(user1).castVote(proposalId, true);
expect(await governance.state(proposalId)).to.equal(ProposalState.Active);
// Wait for voting period to end
await time.increaseTo(startTime + VOTING_PERIOD);
await network.provider.send("evm_mine");
// Verify state is Succeeded
expect(await governance.state(proposalId)).to.equal(ProposalState.Succeeded);
// Queue the proposal
await governance.execute(proposalId);
expect(await governance.state(proposalId)).to.equal(ProposalState.Queued);
// Create a proposal
const targets = [await testTarget.getAddress()];
const values = [0];
const calldatas = [
testTarget.interface.encodeFunctionData("setValue", [42])
];
const tx = await governance.connect(owner).propose(
targets,
values,
calldatas,
"Test Proposal",
0 // ParameterChange
);
const receipt = await tx.wait();
const event = receipt.logs.find(
log => governance.interface.parseLog(log)?.name === 'ProposalCreated'
);
proposalId = event.args.proposalId;
await time.increase(VOTING_DELAY);
await network.provider.send("evm_mine");
await governance.connect(user1).castVote(proposalId, true);
await time.increase(VOTING_PERIOD);
await network.provider.send("evm_mine");
const state = await governance.state(proposalId);
expect(state).to.equal(ProposalState.Queued);
});

Tools Used

Manual review.

Recommendations

Add randomness to the hash using for example network parameters such as the block number or the timestamp.

Updates

Lead Judging Commences

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

Governance generates non-unique timelock operation IDs for different proposals with identical parameters, allowing timelock bypass and proposal DoS attacks

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

Governance generates non-unique timelock operation IDs for different proposals with identical parameters, allowing timelock bypass and proposal DoS attacks

Support

FAQs

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

Give us feedback!