The graduateAndUpgrade function in the LevelOne contract incorrectly calculates teacher payouts during the graduation process. It calculates the total amount intended for all teachers (35% of the bursary) but then attempts to transfer this full amount to each individual teacher in a loop. This results in the contract attempting to pay out significantly more funds than allocated (numberOfTeachers * 35% of the bursary), which will likely cause the transaction to fail due to insufficient balance or, in rare cases of overfunding, drain the contract's bursary excessively.
Background:
The LevelOne contract manages student enrollment, school sessions, and teacher roles. Students pay schoolFees in USDC, which accumulate in the contract's bursary. The graduateAndUpgrade function is intended to be called by the principal at the end of a cycle to distribute a portion of the bursary as wages (35% total to teachers, 5% to the principal) and authorize an upgrade to a LevelTwo contract.
Root Cause:
The vulnerability lies in the calculation and distribution logic for teacher wages within the graduateAndUpgrade function.
The variable payPerTeacher is calculated as (bursary * TEACHER_WAGE) / PRECISION. Given TEACHER_WAGE = 35 and PRECISION = 100, this calculates 35% of the total bursary. This amount represents the total pool of funds designated for all teachers combined.
However, inside the for loop that iterates through listOfTeachers, the contract executes usdc.safeTransfer(listOfTeachers[n], payPerTeacher); for each teacher. This attempts to send the entire 35% share to each teacher individually.
Triggering the Vulnerability:
The principal calls the graduateAndUpgrade function.
The contract calculates payPerTeacher as 35% of the current bursary balance.
The loop begins iterating through the listOfTeachers array.
For the first teacher (n=0), the contract attempts to transfer the full payPerTeacher amount.
If there is a second teacher (n=1), the contract attempts to transfer the same full payPerTeacher amount again to the second teacher.
This continues for all teachers in the list.
Code Snippet:
The impact depends on the number of teachers and the actual bursary balance compared to the intended payout:
Transaction Revert (Denial of Service): This is the most likely outcome if totalTeachers > 1. For example, with 2 teachers, the function attempts to pay out 35% (first teacher) + 35% (second teacher) + 5% (principal) = 75% of the bursary. If the bursary only contains the funds from schoolFees, it likely won't have enough USDC to cover the second teacher's payment after the first one is made (or even the first one if bursary is unexpectedly low). The safeTransfer call will fail due to insufficient balance, causing the entire graduateAndUpgrade transaction to revert. This prevents teachers and the principal from receiving any payment and blocks the contract upgrade process.
Excessive Fund Depletion: In the unlikely scenario where the contract's bursary holds significantly more USDC than expected (e.g., due to direct transfers or calculation errors elsewhere), the initial transfers might succeed. However, the contract will pay out far more than the intended 35% total teacher wage. With 3 teachers, it would attempt to pay 105% of the bursary just to teachers, leading to a severe drain of funds until the balance is exhausted or the transaction finally reverts. This constitutes a critical loss of funds beyond the intended economic design.
In summary, the vulnerability leads either to a denial of service preventing payouts and upgrades or a critical drain of the contract's funds allocated for salaries.
`payPerTeacher` in `graduateAndUpgrade()` is incorrectly calculated.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.