50822 sc high deployer can cpgrade arctoken to malicious implementation and steal all user funds
Submitted on Jul 28th 2025 at 20:31:45 UTC by @holydevoti0n for Attackathon | Plume Network
Report ID: #50822
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
Permanent freezing of funds
Description
Brief/Intro
The ArcTokenFactory contract gives the deployer DEFAULT_ADMIN_ROLE which allows a malicious deployer to upgrade the deployed ArcToken to any malicious implementation.
Vulnerability Details
When deploying a new ArcToken using the createToken function, the deployer is granted the DEFAULT_ADMIN_ROLE and the UPGRADER_ROLE is assigned to the factory.
https://github.com/immunefi-team/attackathon-plume-network/blob/580cc6d61b08a728bd98f11b9a2140b84f41c802/arc/src/ArcTokenFactory.sol#L193
function createToken(
string memory name,
string memory symbol,
uint256 initialSupply,
address yieldToken,
string memory tokenUri,
address initialTokenHolder,
uint8 decimals
) external returns (address) {
...
// Grant the DEFAULT_ADMIN_ROLE to the deployer
token.grantRole(token.DEFAULT_ADMIN_ROLE(), msg.sender);
token.grantRole(token.ADMIN_ROLE(), msg.sender);
token.grantRole(token.MANAGER_ROLE(), msg.sender);
token.grantRole(token.YIELD_MANAGER_ROLE(), msg.sender);
token.grantRole(token.YIELD_DISTRIBUTOR_ROLE(), msg.sender);
token.grantRole(token.MINTER_ROLE(), msg.sender);
token.grantRole(token.BURNER_ROLE(), msg.sender);
token.grantRole(token.UPGRADER_ROLE(), address(this));
...
}Protocol assumes that only the factory can upgrade the token. However, as shown above, the deployer is granted the DEFAULT_ADMIN_ROLE. This role implicitly grants full control over all other roles, including the UPGRADER_ROLE. As a result, the deployer can unilaterally upgrade the token contract.
A malicious deployer would only need to perform the following steps:
Use
grantRole()to assign themselves theUPGRADER_ROLE.Call
upgradeToAndCall()to point the proxy to a malicious implementation.Use the malicious implementation to drain funds, mint tokens, or anything else.
Impact
A malicious deployer can steal or burn all user funds by upgrading the ArcToken contract to a malicious implementation.
References:
https://docs.openzeppelin.com/contracts/5.x/access-control
Proof of Concept
Context
ArcTokenis deployed by the factory.The deployer is granted the
DEFAULT_ADMIN_ROLE.The deployer calls
grantRole()to assign themselves theUPGRADER_ROLE.The deployer calls
upgradeToAndCall()to point the proxy to a malicious implementation.At this point, attacker can do anything with the users' funds (withdraw/burn/freeze).
Attack steps
Create an
ArcTokenvia the factory.Grant
UPGRADER_ROLEto the attacker (authorized viaDEFAULT_ADMIN_ROLE).Deploy a malicious
ArcTokenimplementation.Call
upgradeToAndCall()on the proxy to switch to the malicious implementation.Use the malicious implementation to drain/burn/freeze funds.
Was this helpful?