Open code423n4 opened 1 year ago
this is a valid finding
The issue is well demonstrated, properly formatted, contains a coded POC. Marking as HQ.
0xSorryNotSorry marked the issue as high quality report
0xSorryNotSorry marked the issue as duplicate of #232
alcueca marked the issue as selected for report
ElliotFriedman marked the issue as sponsor confirmed
Lines of code
https://github.com/code-423n4/2023-07-moonwell/blob/main/src/core/Governance/TemporalGovernor.sol#L27 https://github.com/code-423n4/2023-07-moonwell/blob/main/src/core/Governance/TemporalGovernor.sol#L188-L198 https://github.com/code-423n4/2023-07-moonwell/blob/main/src/core/Governance/TemporalGovernor.sol#L256
Vulnerability details
Description
When a
guardian
pausesTemporalGovernor
they lose the ability to pause again. This can then be reinstated by governance usinggrantGuardiansPause
.However
grantGuardiansPause
can be called when the contract is still paused. This would break the assertions inpermissionlessUnpause
andtogglePause
:assert(!guardianPauseAllowed)
.Also,
TemporalGovernor
inherits from OpenZeppelinOwnable
. There theowner
has the ability torenounceOwnership
.This could cause
TemporalGovernor
to end up in a bad state, sincerevokeGuardian
call that has special logic for revoking the guardian.There is the ability to combine these two issues to brick the contract:
guardian
pauses the contract.grantGuardiansPause
which theguardian
executes (while still paused). This step disables bothpermissionlessUnpause
andtogglePause
. Though the contract is still rescuable throughrevokeGuardian
.guardian
callsrenounceOwnership
fromOwnable
. Which disables any possibility to execute commands onTemporalGovernor
. Hence no more ability to callrevokeGuardian
.Impact
The contract is bricked. Since
permissionlessUnpause
can't be called due to theassert(!guardianPauseAllowed)
. No cross chain calls an be processed becauseexecuteProposal
iswhenNotPaused
and there is noguardian
to executefastTrackProposalExecution
. Thus theTemporalGovernor
will be stuck in paused state.It is also possible for the
guardian
to hold the contract hostage before step 3 (renounceOwnership
) as they are the only ones who can execute calls on it (throughfastTrackProposalExecution
).This is scenario relies on governance sending a cross chain
grantGuardiansPause
while the contract is still paused and a maliciousguardian
which together are unlikely. Though the docs mention this as a concern:Proof of Concept
Test in
TemporalGovernorExec.t.sol
:Tools Used
Manual audit
Recommended Mitigation Steps
However unlikely this is to happen, there are some simple steps that can be taken to prevent if from being possible:
Consider adding
whenNotPaused
tograntGuardiansPause
to prevent mistakes (or possible abuse). And also overriding the callsrenounceOwnership
andtransferOwnership
.transferOwnership
should be a governance call (msg.sender == address(this)
) to move theguardian
to a new trusted account.Perhaps also consider if the
assert(!guardianPauseAllowed)
is worth just for pleasing the SMT solver.Assessed type
DoS