50713 sc high deployer s default admin role enables self grant of upgrader role bypassing implementation whitelist
Submitted on Jul 27th 2025 at 18:52:08 UTC by @Paludo0x for Attackathon | Plume Network
Report ID: #50713
Report Type: Smart Contract
Report severity: High
Target: https://github.com/immunefi-team/attackathon-plume-network/blob/main/arc/src/ArcTokenFactory.sol
Impacts:
Direct theft of any user funds, whether at-rest or in-motion, other than unclaimed yield
Description
Vulnerability Details
ArcTokenFactory::createToken() gives the external caller msg.sender the DEFAULT_ADMIN_ROLE on every new ArcToken.
Because DEFAULT_ADMIN_ROLE is the admin role of all other roles, the deployer can immediately grant themselves UPGRADER_ROLE and execute upgradeTo() on the token proxy installing any implementation, reviewed or not.
This bypasses the factory’s allowedImplementations whitelist and defeats the intended upgrade safety mechanism — any byte-code can be injected as the new ArcToken implementation.
Impact Details
Category is High because the intended safety flow to allow only trusted upgrades is completely bypassed.
With a new implementation the malicious upgrader could do anything, for instance:
mint arbitrary number of tokens
rug-pull by changing yieldToken to an attacker-owned ERC20 and “distributing” worthless tokens
Recommended Fix
Change the admin of UPGRADER_ROLE to itself or to any other SUPER_ADMIN controlled by the factory's DEFAULT_ADMIN_ROLE address so DEFAULT_ADMIN_ROLE cannot directly self-assign UPGRADER_ROLE.
Example:
This ensures DEFAULT_ADMIN_ROLE cannot grant UPGRADER_ROLE without prior governance.
Proof of Concept
Granting DEFAULT_ADMIN_ROLE to deployer while UPGRADER_ROLE is assigned to factory
In ArcTokenFactory::createToken() the DEFAULT_ADMIN_ROLE is granted to msg.sender while the UPGRADER_ROLE is granted to the same contract address(this):
In AccessControlUpgradeable the DEFAULT_ADMIN_ROLE corresponds to bytes32 0x00, therefore if ADMIN_ROLE has not been changed the DEFAULT_ADMIN_ROLE can grant anyone the role of UPGRADER_ROLE:
In fact in ArcToken only the address corresponding to UPGRADER_ROLE can authorize upgrade of implementation:
The intended address allowed to upgrade the token implementation is the DEFAULT_ADMIN_ROLE of the ArcTokenFactory. This is enforced in ArcTokenFactory::upgradeToken():
Because the deployer gets DEFAULT_ADMIN_ROLE on the token, they can call grantRole(UPGRADER_ROLE, attacker) (or to themselves) and then call UUPSUpgradeable.upgradeTo() to set any implementation — bypassing the allowedImplementations whitelist check performed in the factory-level upgradeToken().
Was this helpful?