Description:
The LevelOne.sol
contract is deployed as an implemntation contract and is not meant to be used directly. Instead a proxy delegates calls to it. However, the implementation contract is still on-chain so anyone can call #initialize
because it's public and unprotected.
Impact:
The initializer of the contract can become the principal
and gain access to onlyPrincipal
functions such as #_authorizeUpgrade
. This could allow an attacker to replace the implementation contract with malicious logic to drain funds.
Proof of Concept:
The logic contract is deployed and never initialized.
Attacker calls initialize() → becomes principal.
Attacker calls upgradeToAndCall(maliciousImpl, data) via the proxy → allowed because they are now principal.
maliciousImpl has a selfdestruct or token drain.
Proxy is now broken, or funds are stolen.
Tools
Slither
Foundry
Recommended Mitigation:
OpenZeppelin recommends a constructor-based protection pattern to block calls to initialize() on the logic contract directly:
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.