58189 sc low two step mechanism to transfer ownership is broken due to incorrect access control
Submitted on Oct 31st 2025 at 09:24:19 UTC by @KKam86 for Audit Comp | Alchemix V3
Report ID: #58189
Report Type: Smart Contract
Report severity: Low
Target: https://github.com/alchemix-finance/v3-poc/blob/immunefi_audit/src/AlchemistCurator.sol
Impacts:
Contract fails to deliver promised returns, but doesn't lose value
Description
Brief/Intro
Implementation of two-step mechanism to transfer ownership in AlchemistCurator contract doesn't work as intended because function acceptAdminOwnership() have access control modifier onlyAdmin. In result only current admin can call acceptAdminOwnership(). This function should be called by new pending admin to accept new privileges.
Vulnerability Details
Two-step mechanism to transfer ownership consists of two functions which must be called sequentially in correct order:
Current admin transfers his privileges (ownership) to another address by calling
transferAdminOwnerShip(). This new address is assigned topendingAdminvariable. This address must accept new privileges.New pending admin must accept ownership by making a call to
acceptAdminOwnership(). This function should be reserved only topendingAdmin.
However second step of transfering ownership process in AlchemistCurator is wrongly implemented. Function acceptAdminOwnership() have access control modifier onlyAdmin and in result only current admin can call this function:
As an example, OpenZeppelin's Ownable2Step contract implements two-step mechanism for transfering ownership in the following, correct way:
Current owner calls
transferOwnership()and propose new pending admin:
New pending admin accepts the ownership transfer by making a call to
acceptOwnership():
Also transferOwnership() function emit additional event OwnershipTransferStarted to signal the begining of ownership transfer process.
Impact Details
No protection against common mistakes, such as transfers of ownership to incorrect accounts, or to contracts that are unable to interact with the permission system.
Risk of permanent DOS. If current admin accepts wrong address (which is unable to interact with
AlchemistCuratorcontract) by mistake, then all functions withonlyAdminmodifier will always revert. There is no way to undo the changes (only redeploying the newAlchemistCuratorcontract).
References
https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/Ownable2Step.sol
https://github.com/alchemix-finance/v3-poc/blob/immunefi_audit/src/AlchemistCurator.sol?utm_source=immunefi#L31
https://github.com/alchemix-finance/v3-poc/blob/immunefi_audit/src/utils/PermissionedProxy.sol#L17-L20
Proof of Concept
Proof of Concept
Place following test in AlchemistCurator.t.sol file:
Output:
Was this helpful?