Hawk High

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

Missing access control in `initialize()` in `LevelOne.sol`

Summary

The initialize() function lacks proper access control and is prone to unauthorized initialization and reinitialization risk. Bad actors can hijack the contract and reset important parameters such as the principal address and usdc address.

Vulnerability Details

The initialize() function is supposed to be called once during deployment. If it is not protected by a constructor that disables extra initializations, a bad actor can call initialize() repeatedly. This would allow them to set themselves as the principal, use a malicious token address as _usdcAddress and also manipulate the contract to drain funds.

Example of attack:

Initial deployer sets principal

Attacker calls the initialize() again, resetting important parameters.

All the controls and funds after this are then controlled by the attacker.

function initialize(address _principal, uint256 _schoolFees, address _usdcAddress) public initializer {
if (_principal == address(0)) {
revert HH__ZeroAddress();
}
if (_schoolFees == 0) {
revert HH__ZeroValue();
}
if (_usdcAddress == address(0)) {
revert HH__ZeroAddress();
}
principal = _principal;
schoolFees = _schoolFees;
usdc = IERC20(_usdcAddress);
__UUPSUpgradeable_init();
}

Impact

  • Attacker can set principal or usdc to a malicious address.

  • Attacker takes full control of the contract.

  • Attacker manipulates schoolFees to steal user deposits.

Tools Used

  • Manual code review

  • Slither/Static Analysis

Recommendations


  • Consider locking the implementation contract by using the _disableInitializers() function

constructor() {
_disableInitializers();
}

This ensures the implementation contract cannot be initialized after deployment, eliminating the risk of unauthorized initialization.


  • Implement Ownership initialization using __Ownable_init()

Using __Ownable_init() establishes a clear admin for the contract, and thus locks administrative functions.

function initialize(address _principal, uint256 _schoolFees, address _usdcAddress) public initializer {
if (_principal == address(0)) {
revert HH__ZeroAddress();
}
if (_schoolFees == 0) {
revert HH__ZeroValue();
}
if (_usdcAddress == address(0)) {
revert HH__ZeroAddress();
}
principal = _principal;
schoolFees = _schoolFees;
usdc = IERC20(_usdcAddress);
__Ownable_init(); //implementation
__UUPSUpgradeable_init();
}
Updates

Lead Judging Commences

yeahchibyke Lead Judge
6 months ago
yeahchibyke Lead Judge 6 months 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.