Open sherlock-admin opened 1 year ago
This issue is also resolved by using a dynamic signerCount() function instead of relying on the signerCount state var. This is implemented in https://github.com/Hats-Protocol/hats-zodiac/pull/6, with an additional test to demonstrate added in https://github.com/Hats-Protocol/hats-zodiac/pull/7
zobront added "Fix Approved" label
obront
medium
Safe threshold can be set above target threshold, causing transactions to revert
Summary
If a
targetThreshold
is set below the safe's threshold, thereconcileSignerCount()
function will fail to adjust the safe's threshold as it should, leading to a mismatch that causes all transactions to revert.Vulnerability Detail
It is possible and expected that the
targetThreshold
can be lowered, sometimes even lower than the current safe threshold.In the
setTargetThreshold()
function, there is an automatic update to lower the safe threshold accordingly. However, in the event that thesignerCount < 2
, it will not occur. This could easily happen if, for example, the hat is temporarily toggled off.But this should be fine! In this instance, when a new transaction is processed,
checkTransaction()
will be called, which callsreconcileSignerCount()
. This should fix the problem by resetting the safe's threshold to be within the range ofminThreshold
totargetThreshold
.However, the logic to perform this update is faulty.
As you can see, in the event that the
validSignerCount
is lower than the target threshold, we update the safe's threshold tovalidSignerCount
. That is great.In the event that
validSignerCount
is greater than threshold, we should be setting the safe's threshold totargetThreshold
. However, this only happens in theelse if
clause, whencurrentThreshold < target
.As a result, in the situation where
target < current <= validSignerCount
, we will leave the current safe threshold as it is and not lower it. This results in a safe threshold that is greater thantargetThreshold
.Here is a simple example:
if
block above (validSignerCount <= target && validSignerCount != currentThreshold
) fails becausevalidSignerCount > target
else if
block above (validSignerCount > target && currentThreshold < target
) fails becausecurrentThreshold > target
newThreshold == 0
and the safe isn't updatedIn the
checkAfterExecution()
function that is run after each transaction, there is a check that the threshold is valid:The
_getCorrectThreshold()
function checks if the threshold is equal to the valid signer count, bounded by theminThreshold
on the lower end, and thetargetThreshold
on the upper end:Since our threshold is greater than
targetThreshold
this check will fail and all transactions will revert.Impact
A simple change to the
targetThreshold
fails to propagate through to the safe's threshold, which causes all transactions to revert.Code Snippet
https://github.com/Hats-Protocol/hats-zodiac/blob/9455cc0957762f5dbbd8e62063d970199109b977/src/HatsSignerGateBase.sol#L95-L114
https://github.com/Hats-Protocol/hats-zodiac/blob/9455cc0957762f5dbbd8e62063d970199109b977/src/HatsSignerGateBase.sol#L183-L217
Tool used
Manual Review
Recommendation
Edit the if statement in
reconcileSignerCount()
to always lower to thetargetThreshold
if it exceeds it: