-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Guardian can be DoSed by submitting many transactions at once #4
Comments
I think the unbounded pause is a valid concern, I'll crunch the numbers on what it will take to do vs cancel Considering that some chains have different max gas (Need to check scope), this could be a bigger concern for chains that have a cap below 30MLN (IIRC OP is capped at 15 MLN) Overall this is a legitimate concern |
@GalloDaSballo lmk what you find on the PoC side. |
I believe this finding to be valid, as a Medium Severity bug I will follow up with math, but fundamentally:
I believe this can be elegantly mitigated by using the time at which the pause ends, and making any proposal that is created or altered before that marked as invalid. Please double check my logic @ElliotFriedman |
GalloDaSballo changed the severity to 2 (Med Risk) |
I think the argument for Med is:
The argument for High would be:
Will need to write the code for this specifically:
If Defend consumes less gas than that means that attack requires more than one block, meaning it could be prevented Whereas if they consume similar amounts of gas the attack could have not been prevented via pausing |
GalloDaSballo marked issue #24 as primary and marked this issue as a duplicate of 24 |
mitigated solidity-labs-io/kleidi#53 |
GalloDaSballo marked the issue as partial-75 |
Lines of code
https://github.com/code-423n4/2024-10-kleidi/blob/main/src/Timelock.sol#L687
Vulnerability details
Impact
The Guardian role is supposed to pause the timelock in case of compromised cold signer keys, as explained in the docs. The pause() function will pause the contract and remove all current live proposals, allowing time for a Recovery spell to rotate to a new set of signers. However, the pause() function does an unbounded loop over the current live proposals, an array that the attackers can add any number of entries to. The attackers can flood the array with entries, essentially making calls to function revert due to running out of gas, thus DoSing the Guardian and essentially rendering the role useless.
If the team does not notice the malicios activity on time and the timelock is not deployed/executed before its too late, the attackers can do serious damage, like draining of funds/complete takeover of the system. Since the guardian role is rendered useless, the protocol will be in a race with the clock to save itself.
Proof of Concept
We can see that the pause function will load the _liveProposals array into memory and iterate over all of its entries, removing each of them from the unbounded array:
https://github.com/code-423n4/2024-10-kleidi/blob/main/src/Timelock.sol#L687
Consider the following attack scenario:
Also, note that the malicios signers can frontrun calls to Timelock::cancel since Safe::execTransaction uses a nonce to check validity. By increasing the nonce in a frontrunning transaction they will make honest transactions revert. So, honest signers can be blocked from manually canceling the malicios transactions.
The protocol team will have the last option of rotating out the signers using a recovery spell. However, if this action is not taken before the time left for the malicios transcation to become valid is less than the recovery spell delay, the malicios transactions will go through.
Furthermore, while only the Safe can schedule tranasactions, anyone can execute them. This means that even if the protocol manages to execute the recovery spell on time, they will still be in a race with the clock to cancel all pending malicios transactions. There is no function to batch cancel, only the cancel function to cancel individual transactions, while the attackers have the option to batch schedule. They can submit any number of transactions before the recovery spell comes into effect. As a result the honest signers may not have enough time to cancel all transactions before the timelock delay exprises and a malicios tranasction is executed taking over the protocol.
Recommended Mitigation Steps
Move the logic to remove entries from the live proposals contract into a separate function that is callable only when the contract is paused - say emergencyBatchCancel(). The function must have the functionality to remove a specifiable number of entries, so that it is not DoSable. Also, consider adding a normal batchCancel function for easier cleanup in general.
Assessed type
DoS
The text was updated successfully, but these errors were encountered: