Hawk High

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

[H-3] Unprotected graduate() Function in LevelTwo

Severity

Critical

Impact

The graduate() function in LevelTwo is completely unprotected and can be called by any address, allowing attackers to reset or manipulate contract state during the critical initialization phase. This vulnerability could lead to unauthorized state manipulation, fund theft, and potentially render the entire school system non-functional after upgrade.

Description

LevelTwo includes a graduate() function marked with reinitializer(2), which is intended to initialize the contract state after upgrade:

// From LevelTwo.sol
function graduate() public reinitializer(2) {}

This function has three critical security issues:

  1. It's completely unprotected - any address can call it, not just the proxy during upgrade

  2. It's currently empty - it doesn't perform any initialization despite being a reinitializer

  3. It doesn't accept any parameters - there's no way to pass state from LevelOne to LevelTwo

The reinitializer(2) modifier means this function can be called once to initialize the contract's state at version 2. However, without access controls, an attacker could call this function before the legitimate upgrade process occurs, effectively "stealing" the initialization and preventing the proper migration of state from LevelOne.

Moreover, even during a legitimate upgrade, the empty function body means no proper state migration occurs. According to the protocol documentation, LevelTwo should handle the 60% of bursary funds remaining after teacher and principal payments, but there's no mechanism to accomplish this.

Tools Used

Manual code review

Recommended Mitigation

Protect and properly implement the graduate function:

// LevelTwo.sol
- function graduate() public reinitializer(2) {}
+ function graduate(
+ address[] calldata qualifiedStudents,
+ uint256 remainingBursary
+ ) external reinitializer(2) {
+ // Only allow this to be called during the upgrade process
+ // This can be enforced in various ways, such as:
+ // 1. Checking that msg.sender is the proxy address
+ // 2. Using a one-time authorization token generated during upgrade
+ // 3. Checking that this is part of the upgrade transaction
+
+ // Initialize remaining bursary
+ bursary = remainingBursary;
+
+ // Copy qualified students (those meeting the cutoff score)
+ for (uint256 i = 0; i < qualifiedStudents.length; i++) {
+ listOfStudents.push(qualifiedStudents[i]);
+ isStudent[qualifiedStudents[i]] = true;
+ }
+
+ // Other necessary initialization
+ }

Additionally, modify LevelOne's graduateAndUpgrade() to properly package and transfer state:

// LevelOne.sol
function graduateAndUpgrade(address _levelTwo, bytes memory data) public onlyPrincipal {
// ... existing validation code ...
+ // Package qualified students (above cutoff score)
+ address[] memory qualifiedStudents = new address[](listOfStudents.length);
+ uint256 qualifiedCount = 0;
+ for (uint256 i = 0; i < listOfStudents.length; i++) {
+ if (studentScore[listOfStudents[i]] >= cutOffScore) {
+ qualifiedStudents[qualifiedCount] = listOfStudents[i];
+ qualifiedCount++;
+ }
+ }
+
+ // Calculate remaining bursary after payments (60%)
+ uint256 totalTeachers = listOfTeachers.length;
+ uint256 payPerTeacher = (bursary * TEACHER_WAGE) / PRECISION;
+ uint256 principalPay = (bursary * PRINCIPAL_WAGE) / PRECISION;
+ uint256 remainingBursary = bursary - (totalTeachers * payPerTeacher) - principalPay;
+
+ // Encode initialization data for LevelTwo
+ bytes memory initData = abi.encodeWithSelector(
+ LevelTwo(_levelTwo).graduate.selector,
+ qualifiedStudents,
+ remainingBursary
+ );
+
+ _authorizeUpgrade(_levelTwo);
+ // Pass initData parameter to upgradeTo function to initialize LevelTwo
+ // ... make payments to teachers and principal ...
}
Updates

Lead Judging Commences

yeahchibyke Lead Judge
about 2 months ago
yeahchibyke Lead Judge
about 2 months ago
yeahchibyke Lead Judge about 2 months ago
Submission Judgement Published
Validated
Assigned finding tags:

cut-off criteria not applied

All students are graduated when the graduation function is called as the cut-off criteria is not applied.

Support

FAQs

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