#41659 [SC-Insight] Previous owner still hold manager role after ownership transfer
Was this helpful?
Was this helpful?
Submitted on Mar 17th 2025 at 11:19:43 UTC by @merlinboii for
Report ID: #41659
Report Type: Smart Contract
Report severity: Insight
Target: https://github.com/immunefi-team/audit-comp-yeet/blob/main/src/StakeV2.sol
Impacts:
The Manager
contract allows previous owners to retain their manager role even after ownership transfer. This could lead to unauthorized access to manager's privileged functions: executeRewardDistributionYeet()
and executeRewardDistribution()
. If proper role cleanup is not performed manually, potentially compromising the protocol's access control system.
Manager roles handling in the Manager
contract are not automatically revoked during ownership transfer:
In the constructor, both owner and manager get manager roles:
When ownership is transferred using Ownable's transferOwnership
, the previous owner's manager status remains as there is no override logic to cleanup of manager roles:
The removeManager
function exists but must be called manually:
This could be considered a Security best practices
assuming that all setup roles are trusted, including the previous owner, who was trusted before the transfer.
However, if the previous owner was compromised and their address has not yet been removed from the manager role, it creates an opportunity for an attacker to frontrun the removeManager()
and operate crucial functions.
https://github.com/immunefi-team/audit-comp-yeet/blob/main/src/StakeV2.sol#L35-L59
During deployment, Trusted-Owner-A (EOA) is used as the deployer of the contract and holds both owner and manager roles
After setup is complete, ownership is transferred from Trusted-Owner-A to Trusted-Owner-B (The Multisig)
Trusted-Owner-A retains their manager role
Over time, if Trusted-Owner-A's account becomes compromised or experiences key leakage, attacker can still race to call executeRewardDistributionYeet()
and executeRewardDistribution()
to distribute and redirect reward distributions to their own addresses (by setting vaultParams.receiver != StakeV2
)