Hawk High

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

Missing `SessionEnd` Check in `LevelOne::graduateAndUpgrade`, unexpectedly session ended may cause unexpected behaviour

Description

The LevelOne::graduateAndUpgrade function lacks a critical check to verify whether the school session has officially ended (sessionEnd). Without this check, the function can be invoked prematurely by the principal, which could lead to unexpected and potentially dangerous outcomes.

This includes:

  • Premature termination of the current session

  • Unexpected state transitions

  • Potential inconsistencies in logic or data

  • Misuse or loss of bursary funds due to early payouts

Vulnerable Function

function graduateAndUpgrade(address _levelTwo, bytes memory) public onlyPrincipal {
// Missing: require(block.timestamp >= sessionEnd);
if (_levelTwo == address(0)) {
revert HH__ZeroAddress();
}
uint256 totalTeachers = listOfTeachers.length;
uint256 payPerTeacher = (bursary * TEACHER_WAGE) / PRECISION;
uint256 principalPay = (bursary * PRINCIPAL_WAGE) / PRECISION;
_authorizeUpgrade(_levelTwo);
for (uint256 n = 0; n < totalTeachers; n++) {
usdc.safeTransfer(listOfTeachers[n], payPerTeacher);
}
usdc.safeTransfer(principal, principalPay);
}

Impact

  • Security Risk: The contract allows an upgrade before the intended session duration.

  • Data Corruption: Logic tied to session length or order of operations may break.

  • Financial Risk: Funds may be transferred or locked inappropriately.

Proof of Concept

function test_confirm_OwnerCanCompleteSessionBeforeTime() public schoolInSession {
vm.warp(block.timestamp + 1 weeks); // Session expected to last 4 weeks
levelTwoImplementation = new LevelTwo();
levelTwoImplementationAddress = address(levelTwoImplementation);
bytes memory data = abi.encodeCall(LevelTwo.graduate, ());
vm.prank(principal);
levelOneProxy.graduateAndUpgrade(levelTwoImplementationAddress, data);
LevelTwo levelTwoProxy = LevelTwo(proxyAddress);
console2.log(levelTwoProxy.bursary());
console2.log(levelTwoProxy.getTotalStudents());
}

Recommended Mitigation

Add a require statement at the beginning of graduateAndUpgrade:

function graduateAndUpgrade(address _levelTwo, bytes memory) public onlyPrincipal {
require(block.timestamp >= sessionEnd, "HH: Session has not ended");
if (_levelTwo == address(0)) {
revert HH__ZeroAddress();
}
uint256 totalTeachers = listOfTeachers.length;
uint256 payPerTeacher = (bursary * TEACHER_WAGE) / PRECISION;
uint256 principalPay = (bursary * PRINCIPAL_WAGE) / PRECISION;
_authorizeUpgrade(_levelTwo);
for (uint256 n = 0; n < totalTeachers; n++) {
usdc.safeTransfer(listOfTeachers[n], payPerTeacher);
}
usdc.safeTransfer(principal, principalPay);
}
Updates

Lead Judging Commences

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

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.