Recovery Failure Due to Ablity to Set Contracts as RecoverySpell Owners #13
Labels
bug
Something isn't working
downgraded by judge
Judge downgraded the risk level of this issue
grade-c
primary issue
Highest quality submission among a set of duplicates
QA (Quality Assurance)
Assets are not at risk. State handling, function incorrect as to spec, issues with clarity, syntax
🤖_01_group
AI based duplicate group recommendation
sponsor confirmed
Sponsor agrees this is a problem and intends to fix it (OK to use w/ "disagree with severity")
sufficient quality report
This report is of sufficient quality
unsatisfactory
does not satisfy C4 submission criteria; not eligible for awards
Lines of code
https://github.com/solidity-labs-io/kleidi/blob/0d72b6cb5725c1380212dc76257da96fcfacf22f/src/RecoverySpellFactory.sol#L37-L78
Vulnerability details
Description
The
createRecoverySpell()
function in theRecoverySpellFactory
contract, allows setting contracts as owners of aRecoverySpell
. When initiating a recovery, owners need to sign a message, but since contracts cannot sign messages like EOAs, this will cause the recovery of safe to be impossible. If the recovery cannot be initiated, the safe and timelock will become unusable and leaving the safe and timelock unmanageable forever.Impact
Setting contracts as owners in a
RecoverySpell
will cause the recovery process to fail because signatures are required for initiating recovery. this makes it impossible to recover the safe and as result timelock funds will be locked, since safe is the one that has ablity to manage timelock funds.Proof Of Concept
On #L58-L71, we do owner validation, but we dont make sure the owners are EOA's, capable of signing messages, so they can initiate successful recovery of safe:
Recommended Mitigation
add a check in the
createRecoverySpell()
function to ensure that all owners are externally owned accounts (EOAs) and not contracts.This can be done by implementing a helper function
isContract()
inside theRecoverySpellFactory
that checks if an address is a contract or not:Add this check during the owner validation loop to prevent contracts from being set as owners of RecoverySpell:
function createRecoverySpell( bytes32 salt, address[] memory owners, address safe, uint256 threshold, uint256 recoveryThreshold, uint256 delay ) external returns (RecoverySpell recovery) { _paramChecks(owners, threshold, recoveryThreshold, delay); require(safe.code.length != 0, "RecoverySpell: safe non-existent"); for (uint256 i = 0; i < owners.length; i++) { address owner = owners[i]; bool found; assembly ("memory-safe") { found := tload(owner) if eq(found, 0) { tstore(owner, 1) } } + require(!isContract(owners[i]), "RecoverySpellFactory: Contract owners not allowed"); require(!found, "RecoverySpell: Duplicate owner"); } recovery = new RecoverySpell{salt: salt}( owners, safe, threshold, recoveryThreshold, delay ); emit RecoverySpellCreated(address(recovery), address(safe)); }
This will ensure that all owners are EOAs, capable of signing necessary message, and as result be able to have successful recovery of safe.
Assessed type
Other
The text was updated successfully, but these errors were encountered: