The ProxyFactory
contract inherently implements the renounceOwnership()
, which is risky. Due to possible human error, the owner-privileged functions using the onlyOwner
modifier can be locked permanently.
The ProxyFactory
contract inherits from OpenZeppelin's Ownable
contract. The Ownable
implements the renounceOwnership()
that can permanently remove the ProxyFactory
contract’s ownership.
If an owner (protocol admin) mistakenly invokes the renounceOwnership()
, they will immediately lose ownership of the ProxyFactory
contract, and this action cannot be undone.
The renounceOwnership()
: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/a5ed318634016a25be4000ee07044a31f363e60c/contracts/access/Ownable.sol#L73-L75
The _transferOwnership()
: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/a5ed318634016a25be4000ee07044a31f363e60c/contracts/access/Ownable.sol#L92-L96
The following owner-privileged functions using the onlyOwner
modifier will be locked permanently.
To clarify the vulnerability, although only an owner can execute the renounceOwnership()
and the owner is trusted, the incident can occur by mistake (i.e., this vulnerability is not about any centralization or trust risks; it is about the risks of function invocation mistakes only).
The likelihood is considered LOW (since the owner is expected to do due diligence). The impact is considered HIGH. Therefore, the severity is considered MEDIUM.
Manual Review
If the renounceOwnership()
is not meant to be used, turn off its functionality by overriding the function and executing the revert()
when the function is called.
If the renounceOwnership()
is expected to be used, implement a two-step ownership renouncement mechanism. One example of the two-step renouncement mechanism with a confirmation interval works as follows.
An owner calls the initiateOwnershipRenouncement()
. This function will set the initialTimestamp
(using block.timestamp
).
An owner calls the confirmOwnershipRenouncement()
. This function will compare the diffTimestamp
between the initialTimestamp
(in step 1) and the currentTimestamp
(block.timestamp
). If diffTimestamp
<= expectedConfirmationInterval
, the function will renounce the contract’s ownership. Otherwise, the function will revert the transaction.
The two-step ownership renouncement mechanism guarantees that an owner will not remove the contract's ownership by mistake.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.