Hawk High

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

H-03. [H-3] Incomplete Upgrade Validation

Summary

The graduateAndUpgrade() function in the LevelOne contract is vulnerable to premature upgrades and improper access control, allowing the principal to:

  1. Upgrade the contract before the session ends

  2. Bypass student review checks

  3. Withdraw funds prematurely
    This could lead to loss of funds, invalid upgrades, or students being graduated without meeting requirements.

Vulnerability Details

  1. Missing Session-End Validation
    The function does not enforce that the session has ended (block.timestamp >= sessionEnd) before allowing an upgrade.

  2. Incomplete Student Review Checks
    The function does not verify that:
    i. All students have received 4 reviews (1 per week).
    ii. All students meet the cut-off score (studentScore >= cutOffScore).

  3. Missing Upgrade Validation
    No checks ensure that:
    i. The new implementation (_levelTwo) is valid (e.g., not a malicious contract).
    ii. The upgrade data (bytes memory) is properly formatted.

Proof of Concept

function test_prematureUpgrade() public {
// Force session to appear active
vm.warp(block.timestamp + 1 days); // Fast-forward 1 day
vm.store(
address(levelOneProxy),
bytes32(uint256(keccak256("sessionEnd"))),
bytes32(uint256(block.timestamp + 1 weeks)) // Set future end
);
// Attempt upgrade
address levelTwo = address(new LevelTwo());
vm.prank(principal);
levelOneProxy.graduateAndUpgrade(levelTwo, "");
// Verify improper upgrade
assertEq(
levelOneProxy.getSessionStatus(),
false,
"Session should be ended"
);
}

Impact

  1. Principal/teachers could withdraw funds mid-session

  2. Students could be graduated before reviews complete

Tools Used

  1. Foundry

  2. VS Code

Recommendations

  1. Add Session Validation

require(block.timestamp >= sessionEnd, "Session not ended");
  1. Enforce Review Completion

for (uint i = 0; i < listOfStudents.length; i++) {
require(reviewCount[listOfStudents[i]] >= 4, "Missing reviews");
require(studentScore[listOfStudents[i]] >= cutOffScore, "Score too low");
}
  1. Secure Fund Distribution

// After all checks...
uint256 payPerTeacher = (bursary * TEACHER_WAGE) / PRECISION;
for (uint i = 0; i < listOfTeachers.length; i++) {
usdc.safeTransfer(listOfTeachers[i], payPerTeacher);
}
  1. Validate Upgrade Target

require(newImpl.code.length > 0, "Invalid implementation");
  1. Use OpenZeppelin’s Upgrade Safety

import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
function _authorizeUpgrade(address newImpl) internal override onlyPrincipal {
require(block.timestamp >= sessionEnd, "Session not ended");
// Add additional checks...
}
Updates

Lead Judging Commences

yeahchibyke Lead Judge
about 2 months ago
yeahchibyke Lead Judge about 1 month 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.

can graduate without session end

`graduateAndUpgrade()` can be called successfully even when the school session has not ended

Support

FAQs

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