The LevelOne implementation contract is missing the _disableInitializers function call which allows an attacker to directly call the LevelOne::initialize function with arbitrary values taking over the implementation contract.
A smart contract that makes use of the UUPSUpgradeable pattern, needs to lock the implementation contract so that is can only be initialized via the proxy.
If a upgradable contract is not locked down after deployment, its functions can be directly accessed and it is therefore vulnerable to being taken over by an attacker. OpenZeppelin provides the _disableInitializers function which is to be invoked in the constructor of the implementation contract effectively locking down the contract and prevent abuse. The related security guideline can be found at: "https://docs.openzeppelin.com/contracts/5.x/api/proxy#Initializable"
The Code Issue
The LevelOne contract is missing a constructor and therefore cannot invoke the _disableInitializers function to lock down the implementation contract.
As a result, once the implementation contract is deployed, as it is not locked down, an attacker can directly call LevelOne::initialize and takeover the implementation contract breaking functionality with arbitrary values.
Note: while the contract deployment script initializes the implementation contract directly, it is not advisable and risky to have the implementation contract deployed in an unlocked state. As the deployment script may change, it is good for the implementation contract to have its own protection.
The Affected Code Area
Below is a snippet from the beginning of the LevelOne contract starting from line 86.
The severity of this vulnerability is High/Medium as it allows an attacker to takeover the implementation contract and claim ownership as the ownership. An attacker, can
Set higher/lower fees than intended
Set themselves as the Principal effectively gaining control over sensitive functions
Use a different ERC20 Token other than the expected USDC.
Cause a redeployment of the implementation contract resulting in financial expenses for Hawk High
The likelihood however, is likely low.
Manual Review
Foundry
Description
In order to prove the validity of this vulnerability, I have created a runnable PoC that
Deploys the implementation contract
Directly calls the initialize function on the implementation contract setting arbitrary values
Run: forge test --mt testImplementationContractTakeOver -vvv
Code
The system can be re-initialized by an attacker and its integrity tampered with due to lack of `disableInitializer()`
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.