The initialize() function in LevelOne contract is publicly accessible and lacks any form of access control or front-run protection. This allows any external actor to front-run the intended deployer and invoke the initialize() function first, setting arbitrary initialization parameters and taking control over contract-critical state.
In upgradeable contract systems using proxy patterns, the initialize() function replaces the constructor and is meant to be called once—typically immediately after the proxy is deployed. If this function is left unprotected, malicious actors monitoring mempool transactions could front-run the deployer and call initialize() first.
In this case, the initialize() function accepts parameters such as _principal, _schoolFees, and _usdcAddress, which directly influence the contract’s core logic and access control. Since the function uses the initializer modifier, it can only be called once per contract instance—meaning once a malicious actor calls it, the legitimate deployer will be permanently blocked from initializing the contract correctly.
This effectively grants the attacker permanent control over roles or funds, or renders the contract unusable.
Unauthorized actors can set _principal, _schoolFees, and _usdcAddress arbitrarily.
Legitimate deployer is permanently blocked from initializing the contract.
Full control of the contract logic may be hijacked by malicious actors.
Potential financial or operational loss depending on contract function.
Manual code review
To prevent front-running of the initialize() function and ensure the contract is initialized only by the intended party, consider one of the below solutions:
Option 1: initialize() function checks the invoker address is known trusted address. (not recommended, hardcoded values brings less flexability)
Option 2: When using ERC1967Proxy, ensure that the initialize() function is invoked during deployment.
By encoding the initialization call in the _data argument passed to the proxy’s constructor. This guarantees that initialization occurs in the same transaction as the proxy deployment, eliminating any front-run risk.
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.