Hawk High

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

LevelOne.graduateAndUpgrade() can be called at any time, bypassing the sessionEnd requirement

Summary

LevelOne.graduateAndUpgrade() should check if sessionEnd has passed. In the absence of this check, the contract can be upgraded early, breaking one of the contract invariants.

Vulnerability Details

LevelOne.graduateAndUpgrade() is an onlyPrincipal function that is meant to be called at the end of a school session. The code and docs specify the length of one session to be 4 weeks. However, there is no time check in the function to ensure this behavior.

Relevant code:

function graduateAndUpgrade(address _levelTwo, bytes memory) public onlyPrincipal {
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

The principal can end the session in less than the required 4 weeks, breaking a contract invariant.

Proof Of Concept

LevelOneAndGraduateTeat.t.sol already has a test that demonstrates this behavior. forge test --mt test_confirm_can_graduate -vvv

function test_confirm_can_graduate() public schoolInSession {
levelTwoImplementation = new LevelTwo();
levelTwoImplementationAddress = address(levelTwoImplementation);
bytes memory data = abi.encodeCall(LevelTwo.graduate, ());
vm.prank(principal);
// schoolInSession starts the session and graduateAndUpgrade() can end it immediately.
// The contract also allows an upgrade if a session has never been started, even though there would be no point to doing that.
levelOneProxy.graduateAndUpgrade(levelTwoImplementationAddress, data);
LevelTwo levelTwoProxy = LevelTwo(proxyAddress);
console2.log(levelTwoProxy.bursary());
console2.log(levelTwoProxy.getTotalStudents());
}

Expected Result:

Ran 1 test for test/LeveOnelAndGraduateTest.t.sol:LevelOneAndGraduateTest
[PASS] test_confirm_can_graduate() (gas: 1172920)
Logs:
30000000000000000000000
6
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.79ms (564.05µs CPU time)
Ran 1 test suite in 9.87ms (2.79ms CPU time): 1 tests passed, 0 failed, 0 skipped (1 total tests)

Tools Used

Manual Review

Recommendation

Before executing the function, check if inSession is true and sessionEnd has passed.

Updates

Lead Judging Commences

yeahchibyke Lead Judge 16 days 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

yeahchibyke Lead Judge 16 days 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.