Hawk High

First Flight #39
Beginner FriendlySolidity
100 EXP
View results
Submission Details
Impact: high
Likelihood: high
Invalid

Permanent Loss of School Funds During Upgrade Implementation

Summary

The LevelOne to LevelTwo contract upgrade implementation results in permanent loss of 60% of all collected school fees. The funds remain trapped in the proxy contract due to architectural flaws in the upgrade design and missing withdrawal functionality in the LevelTwo contract.

Vulnerability Details

The vulnerability stems from incomplete funds distribution during the upgrade process combined with the absence of fund management mechanisms in the target contract:


  1. Partial Fund Distribution: In the graduateAndUpgrade() function, only 40% of collected funds (bursary) are distributed:

// LevelOne.sol:395-396
uint256 payPerTeacher = (bursary * TEACHER_WAGE) / PRECISION; // 35%
uint256 principalPay = (bursary * PRINCIPAL_WAGE) / PRECISION; // 5%

2.No Withdrawal Mechanism: LevelTwo lacks any functionality to retrieve the remaining funds:

solidity

// LevelTwo.sol - no withdrawal functions exist
contract LevelTwo is Initializable {
// ... storage variables ...
// View functions only, no fund management functions
function getPrincipal() external view returns (address) { ... }
function getSchoolFeesToken() external view returns (address) { ... }
// ... more view functions ...
}

Impact

High Severity: The issue leads to direct and permanent financial loss for the system.


Quantitative Analysis:

  • With school fees of 1 ETH and 100 students: 60 ETH permanently locked

  • With school fees of 0.1 ETH and 1000 students: 60 ETH permanently locked

This is classified as high severity because:

  1. The loss is unavoidable with the current implementation

  2. The issue directly contradicts the documented business logic requirement that "remaining 60% should reflect in the bursary after upgrade"

  3. The loss scales linearly with adoption (more students = more lost funds)

  4. No recovery mechanism exists

Tools Used

Manual inspection of both contract implementations

Recommendations

  1. Implement Fund Management in LevelTwo: Add functions to handle funds in the new implementation:

// Add to LevelTwo.sol
modifier onlyPrincipal() {
require(msg.sender == principal, "Not authorized");
_;
}
function withdrawFunds(address recipient, uint256 amount) external onlyPrincipal {
require(recipient != address(0), "Zero address");
require(amount > 0, "Zero amount");
require(amount <= usdc.balanceOf(address(this)), "Insufficient funds");
usdc.safeTransfer(recipient, amount);
}
Updates

Lead Judging Commences

yeahchibyke Lead Judge 6 months ago
Submission Judgement Published
Invalidated
Reason: Design choice

Support

FAQs

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