The Governance contract uses a non-unique descriptionHash as the salt parameter when scheduling operations in the TimelockController. This allows attackers to create multiple proposals with identical operation IDs, enabling denial-of-service (DoS) attacks where malicious actors can block legitimate proposals from execution or disrupt governance processes indefinitely.
Affected Code:
In Governance.sol, the _queueProposal function generates the salt for Timelock operations using only the proposal’s descriptionHash:
Root Cause:
The salt is not bound to the proposal’s unique identifier (proposalId). Attackers can exploit this by:
Creating multiple proposals with identical targets, values, calldatas, and colliding descriptionHash.
Triggering a single operation ID in the Timelock for all such proposals.
Example Attack:
Proposal Creation:
User A submits Proposal 1 with parameters (targets, values, calldatas, descriptionA).
User B submits Proposal 2 with the same (targets, values, calldatas) and a crafted descriptionB such that keccak256(descriptionB) = keccak256(descriptionA).
Operation ID Collision:
Both proposals generate the same Timelock operation ID because salt = descriptionHash.
Blocking Legitimate Execution:
User B executes Proposal 2, queuing the operation in the Timelock.
User A attempts to execute Proposal 1 but fails due to the isOperationPending check.
User B cancels the operation via TimelockController.cancel(), permanently blocking Proposal 1.
Repeatable DoS:
User B repeats this process for every new proposal from User A, forcing continuous re-submissions.
Critical Severity:
Governance Paralysis: Legitimate proposals can be permanently blocked.
Funds at Risk: If proposals include time-sensitive transactions (e.g., security patches), delays could lead to exploits.
Gas Griefing: Attackers waste victims’ gas by forcing proposal resubmissions.
Manual code review
Bind Salt to Proposal ID:
Modify the salt to include the proposalId, ensuring uniqueness:
Track Proposal Hashes:
Add a mapping to reject duplicate proposals:
Fail-Early in Timelock:
In TimelockController.scheduleBatch(), revert if salt is reused:
By implementing these fixes, the system will ensure unique operation IDs and prevent hash collision-based attacks.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.