For the complete documentation index, see llms.txt. This page is also available as Markdown.

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 the UPGRADER_ROLE.

  • Call upgradeToAndCall() to point the proxy to a malicious implementation.

  • Use the malicious implementation to drain funds, mint tokens, or anything else.

Impact

References:

  • https://docs.openzeppelin.com/contracts/5.x/access-control

Proof of Concept

Context

  • ArcToken is deployed by the factory.

  • The deployer is granted the DEFAULT_ADMIN_ROLE.

  • The deployer calls grantRole() to assign themselves the UPGRADER_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).

1

Attack steps

  • Create an ArcToken via the factory.

  • Grant UPGRADER_ROLE to the attacker (authorized via DEFAULT_ADMIN_ROLE).

  • Deploy a malicious ArcToken implementation.

  • Call upgradeToAndCall() on the proxy to switch to the malicious implementation.

  • Use the malicious implementation to drain/burn/freeze funds.

PoC test (add to ArcTokenFactory.t.sol)

Run:

Output:

Was this helpful?