#41659 [SC-Insight] Previous owner still hold manager role after ownership transfer
Submitted on Mar 17th 2025 at 11:19:43 UTC by @merlinboii for Audit Comp | Yeet
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:
Description
Brief/Intro
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.
Vulnerability Details
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:
Impact Details
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.
References
https://github.com/immunefi-team/audit-comp-yeet/blob/main/src/StakeV2.sol#L35-L59
Proof of Concept
Proof of Concept
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()
andexecuteRewardDistribution()
to distribute and redirect reward distributions to their own addresses (by settingvaultParams.receiver != StakeV2
)
Was this helpful?