Hawk High

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

There is no checks in `LevelOne:graduateAndUpgrade` or `_authorizeUpgrade` or `upgradeToAndCall` that school session lasted four weeks, allowing principal to upgrade contract before four week

Description: One of the protocol invariant is that the school session lasts four weeks but the principal can call graduateAndUpgrade function or upgradeToAndCall and upgrade the protocol before reaching four weeks. The pricipal can even upgrade the protocol before session begins.

Vulnerability Details: There is no checks in graduateAndUpgrade if the session lasted four weeks

Impact: Principal can upgrade protocol before four weeks severly breaking the protocol invariant

Tools Used: Manula Review

Proof of Concept: Add this test suit to LevOnelAndGraduateTest.t.sol

Before doing any tests for levelOneProxy.upgradeToAndCall() to work add UUPSUpgradable in LevelTwo and this is a bug for another report

  1. pricipal can end before session end

Proof of Code
function test_CanGraduateAndUpgradeBeforeSessionEnd() public {
vm.startPrank(principal);
levelOneProxy.addTeacher(alice);
levelOneProxy.addTeacher(bob);
vm.stopPrank();
vm.startPrank(clara);
usdc.approve(address(levelOneProxy), schoolFees);
levelOneProxy.enroll();
vm.stopPrank();
vm.startPrank(dan);
usdc.approve(address(levelOneProxy), schoolFees);
levelOneProxy.enroll();
vm.stopPrank();
vm.startPrank(eli);
usdc.approve(address(levelOneProxy), schoolFees);
levelOneProxy.enroll();
vm.stopPrank();
vm.startPrank(fin);
usdc.approve(address(levelOneProxy), schoolFees);
levelOneProxy.enroll();
vm.stopPrank();
vm.startPrank(grey);
usdc.approve(address(levelOneProxy), schoolFees);
levelOneProxy.enroll();
vm.stopPrank();
vm.startPrank(harriet);
usdc.approve(address(levelOneProxy), schoolFees);
levelOneProxy.enroll();
vm.stopPrank();
uint256 cuttOffScroe = 70;
vm.prank(principal);
levelOneProxy.startSession(cuttOffScroe);
bool isSessionOver = block.timestamp > levelOneProxy.getSessionEnd();
bytes memory data = abi.encodeCall(LevelTwo.graduate, ());
vm.startPrank(principal);
levelOneProxy.graduateAndUpgrade(levelTwoImplementationAddress, data);
levelOneProxy.upgradeToAndCall(levelTwoImplementationAddress, data);
vm.stopPrank();
assert(usdc.balanceOf(principal) == 1500 ether);
assert(LevelTwo(proxyAddress).TEACHER_WAGE_L2() == 40);
assert(!isSessionOver);
}
  1. pricipal can end before session begin

Proof of Code
function test_CanGraduateAndUpgradeBeforeSessionBegin() public {
vm.startPrank(principal);
levelOneProxy.addTeacher(alice);
levelOneProxy.addTeacher(bob);
vm.stopPrank();
vm.startPrank(clara);
usdc.approve(address(levelOneProxy), schoolFees);
levelOneProxy.enroll();
vm.stopPrank();
vm.startPrank(dan);
usdc.approve(address(levelOneProxy), schoolFees);
levelOneProxy.enroll();
vm.stopPrank();
vm.startPrank(eli);
usdc.approve(address(levelOneProxy), schoolFees);
levelOneProxy.enroll();
vm.stopPrank();
vm.startPrank(fin);
usdc.approve(address(levelOneProxy), schoolFees);
levelOneProxy.enroll();
vm.stopPrank();
vm.startPrank(grey);
usdc.approve(address(levelOneProxy), schoolFees);
levelOneProxy.enroll();
vm.stopPrank();
vm.startPrank(harriet);
usdc.approve(address(levelOneProxy), schoolFees);
levelOneProxy.enroll();
vm.stopPrank();
// Session has not been started
bool hasSessionStarted = levelOneProxy.getSessionStatus();
bytes memory data = abi.encodeCall(LevelTwo.graduate, ());
vm.startPrank(principal);
levelOneProxy.graduateAndUpgrade(levelTwoImplementationAddress, data);
levelOneProxy.upgradeToAndCall(levelTwoImplementationAddress, data);
vm.stopPrank();
assert(usdc.balanceOf(principal) == 1500 ether);
assert(LevelTwo(proxyAddress).TEACHER_WAGE_L2() == 40);
assert(!hasSessionStarted);
}

Recommendations: use the sessionEnd variable to check if sessionEnded

function graduateAndUpgrade(address _levelTwo, bytes memory) public onlyPrincipal {
+ if(block.timestamp > sessionEnd && sessionEnd > 0) {
+ revert HH__SessionNotEnded();
+ }
if (_levelTwo == address(0)) {
revert HH__ZeroAddress();
}
uint256 totalTeachers = listOfTeachers.length;
// @audit-low: reminder in division
uint256 payPerTeacher = (bursary * TEACHER_WAGE) / PRECISION;
uint256 principalPay = (bursary * PRINCIPAL_WAGE) / PRECISION;
// @audit-high the remaining fee amount does not reflect in the bursary
_authorizeUpgrade(_levelTwo);
for (uint256 n = 0; n < totalTeachers; n++) {
// @audit-high teachers should receive equal division from 35% not 35% each
usdc.safeTransfer(listOfTeachers[n], payPerTeacher);
}
usdc.safeTransfer(principal, principalPay);
// @audit-high there is no way to check the cuttOffScore during upgrading time
}
Updates

Lead Judging Commences

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