When the following veto function is called, the vetoer can cancel any proposals that are not yet executed, which takes effect immediately. The current vetoer can also choose a new vetoer by calling the _setVetoer function below, which takes effect immediately as well. Accidentally choosing a new vetoer who is not reliable is one way to compromise the vetoer role. There is also no guarantee that the vetoer will not become malicious in the future.
The compromised or malicious vetoer is able to cancel any not-yet-executed proposals that deal with the ETH balance transfers, such as for calling the _withdraw function, and cause the funds to be locked in the NounsDAOProxy or NounsDAOExecutor contracts. Moreover, the compromised or malicious vetoer is able to cancel any not-yet-executed proposals that deal with updating implementations, such as for calling the _setImplementation function, and breaks the protocol on purpose. Hence, if the vetoer becomes compromised or malicious, the negative impact is very high.
function _setVetoer(address newVetoer) public {
require(msg.sender == vetoer, 'NounsDAO::_setVetoer: vetoer only');
emit NewVetoer(vetoer, newVetoer);
vetoer = newVetoer;
}
Proof of Concept
The following tests provided by the protocol team already demonstrate how powerful the vetoer is. As long as the proposals are not executed, the vetoer can cancel these. For proposals that are for the critical interactions, such as calling _withdraw and _setImplementation, which even have passed voting and reached the Succeeded state, the vetoer can still cancel them immediately.
To restrict the vetoer's power, the following changes can be considered.
The vetoer is only allowed to cancel a proposal during a defined period after the voting is done. The proposal is not allowed to be executed during this period.
The vetoer is only allowed to cancel the passed proposal when the number of support and against votes are very close or the number of support votes is much higher than the against votes.
The _setVetoer and _burnVetoPower functions are only callable by NounsDAOExecutor, which is the timelock. Calling these functions require the same proposal and voting process.
Thank you for the suggestions.
We are aware of the power that the vetoer currently has and are actively thinking of the risk-benefit of removing it.
I think this can be reduced to lower severity
Lines of code
https://github.com/code-423n4/2022-08-nounsdao/blob/main/contracts/governance/NounsDAOLogicV2.sol#L374-L393 https://github.com/code-423n4/2022-08-nounsdao/blob/main/contracts/governance/NounsDAOLogicV2.sol#L839-L845
Vulnerability details
Impact
When the following
veto
function is called, the vetoer can cancel any proposals that are not yet executed, which takes effect immediately. The current vetoer can also choose a new vetoer by calling the_setVetoer
function below, which takes effect immediately as well. Accidentally choosing a new vetoer who is not reliable is one way to compromise the vetoer role. There is also no guarantee that the vetoer will not become malicious in the future.The compromised or malicious vetoer is able to cancel any not-yet-executed proposals that deal with the ETH balance transfers, such as for calling the
_withdraw
function, and cause the funds to be locked in theNounsDAOProxy
orNounsDAOExecutor
contracts. Moreover, the compromised or malicious vetoer is able to cancel any not-yet-executed proposals that deal with updating implementations, such as for calling the_setImplementation
function, and breaks the protocol on purpose. Hence, if the vetoer becomes compromised or malicious, the negative impact is very high.https://github.com/code-423n4/2022-08-nounsdao/blob/main/contracts/governance/NounsDAOLogicV2.sol#L374-L393
https://github.com/code-423n4/2022-08-nounsdao/blob/main/contracts/governance/NounsDAOLogicV2.sol#L839-L845
Proof of Concept
The following tests provided by the protocol team already demonstrate how powerful the vetoer is. As long as the proposals are not executed, the vetoer can cancel these. For proposals that are for the critical interactions, such as calling
_withdraw
and_setImplementation
, which even have passed voting and reached theSucceeded
state, the vetoer can still cancel them immediately.https://github.com/code-423n4/2022-08-nounsdao/blob/main/test/governance/NounsDAO/V2/vetoing.test.ts#L157-L280
Tools Used
VSCode
Recommended Mitigation Steps
To restrict the vetoer's power, the following changes can be considered.
_setVetoer
and_burnVetoPower
functions are only callable byNounsDAOExecutor
, which is the timelock. Calling these functions require the same proposal and voting process.