Hawk High

First Flight #39
Beginner FriendlySolidity
100 EXP
View results
Submission Details
Severity: medium
Valid

Unprotected upgradable contract, vulnerable to anybody to call `LevelOne::initialize`

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:

  1. The logic contract is deployed and never initialized.

  2. Attacker calls initialize() → becomes principal.

  3. Attacker calls upgradeToAndCall(maliciousImpl, data) via the proxy → allowed because they are now principal.

  4. maliciousImpl has a selfdestruct or token drain.

  5. 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:

Patch
contract LevelOne is Initializable, UUPSUpgradeable {
using SafeERC20 for IERC20;
constructor() {
_disableInitializers(); // 🔒 Prevents logic contract from being initialized
}
...
}
Updates

Lead Judging Commences

yeahchibyke Lead Judge about 1 month ago
Submission Judgement Published
Validated
Assigned finding tags:

contract can be re-initialized

The system can be re-initialized by an attacker and its integrity tampered with due to lack of `disableInitializer()`

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.