The graduateAndUpgrade(address _levelTwo, bytes memory)
function in the LevelOne
contract is intended to update the proxy contract's implementation to LevelTwo
while distributing the accumulated balance (bursary
) among teachers and the principal
.
However, no actual contract update occurs, as the function only calls _authorizeUpgrade(_levelTwo)
, which merely validates that the msg.sender
is authorized to perform the upgrade. This validation alone does not change the proxy's implementation.
As a result, the proxy continues to execute LevelOne
logic, and the new implementation (LevelTwo
) never comes into effect.
This behavior introduces an additional risk: since the implementation is not changed, the graduateAndUpgrade()
function can be called multiple times, redistributing funds from the bursary
each time if available.
The upgrade does not occur, so LevelTwo
functionalities are never activated.
The principal
can abuse the function, calling it multiple times and withdrawing funds repeatedly while the old logic remains operational.
Users and external systems may believe the contract has been upgraded, but internally it remains LevelOne
, creating inconsistencies and operational confusion.
Deploy LevelTwo
, which is expected to be the new implementation.
The principal
encodes the call to the graduate()
function (from LevelTwo
) and executes graduateAndUpgrade(...)
, attempting to update the proxy's implementation.
Call a known function (getPrincipal()
) after the supposed upgrade. This function is defined in both LevelOne
and LevelTwo
.
The execution trace shows that the contract still performs a delegatecall
to the LevelOne
implementation, not LevelTwo
, proving that the upgrade did not occur:
This line clearly indicates that the active logic is still LevelOne
.
Verify that graduateAndUpgrade()
can be executed multiple times, redistributing funds to the principal each time.
Log output:
The principal
's balance increased by 1500 USDC from the 1st to the 2nd execution, confirming that:
The implementation did not change.
The graduateAndUpgrade()
function can be called multiple times.
Manual Review, Foundry
To ensure the contract's implementation is correctly updated, replace the _authorizeUpgrade(...)
call with a call to _upgradeToAndCall(...)
, which includes permission validation, performs the effective upgrade, and optionally calls an initialization function of the new implementation (graduate()
).
The proxy points to the new implementation (LevelTwo
).
The graduate()
function executes correctly.
The system doesn't implement UUPS properly.
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.