#59091 [SC-Low] low firelightvault sol implementation contract does not disable initializers

#59091 [SC-Low] [LOW]: `FirelightVault.sol` Implementation Contract Does Not Disable Initializers

Submitted on Nov 8th 2025 at 15:28:30 UTC by @chief_hunter888 for Audit Comp | Firelight

  • Report ID: #59091

  • Report Type: Smart Contract

  • Report severity: Low

  • Target: https://github.com/firelight-protocol/firelight-core/blob/main/contracts/FirelightVault.sol

  • Impacts:

    • Contract fails to deliver promised returns, but doesn't lose value

Description

[Low] Implementation Contract does not Disable Initializers

Description

The contract FirelightVault.sol is an Initializable.sol children, meant to be used behind a proxy. However, the current implementation has no constructor in which it would call the _disableInitializers() function as recommended by the OpenZepplin, developer of the inherited contracts. As a result anyone can just initialize the implementation contract and thereby take over the contract as the owner. Furthermore, due to the irreversible nature of initializable.sol, it can only be initialized once. This means once a malicious attacker initialized the contract, its storage/state is irrevocably corrupted.

Add a constructor to FirelightVault.sol that calls _disableInitializers():

Impact

Adding the _disableInitializers() function prevents anyone from calling initialize() on the implementation contract directly, ensuring it can only be initialized through a proxy.

Without this, someone could call initialize() on the implementation contract itself This could lead to the implementation being initialized with malicious parameters Even though the proxy would have its own initialization, having an unprotected implementation is a security risk.

Disabling initializers restricts initialization strictly to the intended proxy usage, preventing major security flaws. Given that an initializable contract can only be initialized once, this irrevocably corrupts the state of the Firevault.sol implementation contract.

Attack Vector

  1. Attacker discovers or deploys the implementation contract address

  2. Attacker calls initialize() directly on the implementation contract (not through proxy)

  3. Attacker sets themselves as the DEFAULT_ADMIN_ROLE

  4. Attacker gains full control over the implementation contract

Impact Details

  1. Implementation Contract Takeover: Attacker becomes the admin of the implementation contract

  2. Role Manipulation: Attacker can grant themselves all privileged roles:

    • RESCUER_ROLE

    • PAUSE_ROLE

    • DEPOSIT_LIMIT_UPDATE_ROLE

    • PERIOD_CONFIGURATION_UPDATE_ROLE

    • BLOCKLIST_ROLE

  3. State Manipulation: Attacker can modify implementation state:

    • Change deposit limits

    • Pause the implementation

    • Modify period configurations

  4. Upgrade Confusion: Can cause confusion in upgrade scenarios where the implementation and proxy have different owners

  5. Potential Griefing: While the proxy's state remains separate, this can create operational confusion

Context

Why Initializable.sol is used here

All four of the OpenZeppelin upgradeable contracts that FirelightVault inherits from directly or indirectly inherit from Initializable.sol:

  1. ERC4626Upgradeable Inherits from ERC20Upgradeable Which inherits from Initializable

  2. AccessControlUpgradeable Directly inherits from Initializable abstract contract AccessControlUpgradeable is Initializable, ContextUpgradeable, IAccessControlUpgradeable, ERC165Upgradeable

  3. PausableUpgradeable Directly inherits from Initializable abstract contract PausableUpgradeable is Initializable, ContextUpgradeable

  4. ReentrancyGuardUpgradeable Directly inherits from Initializable

Summary

The FirelightVault implementation contract does not disable initializers in its constructor, allowing an attacker to directly initialize the implementation contract (not through the proxy). This violates the standard OpenZeppelin upgradeable contract pattern and introduces a real takeover risk.

References

  • OpenZeppelin https://docs.openzeppelin.com/upgrades-plugins/writing-upgradeable#initializing-the-implementation-contract

  • OpenZeppelin Proxy Pattern: https://docs.openzeppelin.com/contracts/4.x/api/proxy

  • Initializable: https://docs.openzeppelin.com/contracts/4.x/api/proxy#Initializable

  • Writing Upgradeable Contracts: https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable

Proof of Concept

Malicious Initialization Vulnerability - POC Summary

Quick Overview

This POC demonstrates a significant vulnerability in the FirelightVault contract where an attacker can initialize the implementation contract directly because there is no _disableInitializers() call in the constructor.

Proof of Concept

A comprehensive test suite has been created demonstrating the vulnerability:

Test File Location

Running the POC

Test Results Summary

The POC demonstrates:

Sample Test output

Attack Scenario Demonstrated

  1. Deployment: Implementation contract is deployed

  2. Attack: Attacker calls initialize() directly on implementation

  3. Takeover: Attacker becomes DEFAULT_ADMIN_ROLE

  4. Privilege Escalation: Attacker grants themselves all roles

  5. State Manipulation: Attacker modifies contract state

  6. Impact: Implementation owned by attacker, separate from proxy

Severity Justification

Low because:

  • Violates OpenZeppelin best practices

  • Enables implementation contract takeover

  • Can cause operational confusion

  • May interfere with upgrades

  • Well-documented vulnerability pattern

  • However, does not directly compromise proxy funds

The javascript test file that serves as a POC. Please add to test/malicious_initialization.js

Foundry test contract versions with and without the vulnerability for testing. Please add to path and under the name of: contracts/test/FirelightVaultUpgradeTest.sol.

The javascript test file that serves as a POC. Please add to test/malicious_initialization.js

Hardhat Config JS to update:

Was this helpful?