The LevelOne smart contract contains a critical vulnerability in its UUPS implementation. By missing the essential _disableInitializers() function in its constructor, the contract allows any external address to directly initialize the implementation contract. This creates a dangerous backdoor that enables an attacker to completely destroy the contract system by delegating to malicious code containing a self-destruct instruction.
Let me walk you through a step-by-step demonstration of how this attack unfolds.
When the LevelOne implementation is deployed, it exists as a standalone contract. Without protection, an attacker can directly initialize it and set themselves as principal.
The attacker deploys a contract designed to destroy the implementation:
upgradeToAndCall functionThe attacker calls upgradeToAndCall directly on the implementation contract, passing the address of the malicious self-destruct contract. Since the attacker is recognized as the principal in the implementation's storage, the authorization check passes. The function delegates to the malicious contract, which executes selfdestruct() in the implementation's context.
The direct call to the implementation triggers a delegatecall to the malicious contract, which executes selfdestruct() and destroys the implementation's bytecode. This permanently disables the implementation contract, making all proxy functions that delegate to it inoperable.
Manual review
Add constructor with _disableInitializers() function to implementation contract.
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.