The contract allows the principal
to upgrade the implementation through the UUPS proxy. While the graduateAndUpgrade()
function includes checks and logic for safe upgrades, the current setup does not require upgrades to go through it. This creates a gap that lets the principal
upgrade the contract without triggering important steps like fund distribution.
The contract uses OpenZeppelin’s UUPS upgrade pattern. It restricts upgrades with the _authorizeUpgrade()
function, which checks if the caller is the principal
:
This function protects the upgrade path but doesn’t control how or when the upgrade happens. As a result, the principal
can call upgradeTo()
directly on the proxy. This bypasses graduateAndUpgrade()
, which skipping actions like sending payments and running setup logic. However, The upgrade would still go through, but the expected outcomes would not happen.
This gap lets the principal skip important parts of the upgrade process. If the principal or an attacker with access calls upgradeTo()
directly:
Teachers and the principal won’t get paid
Setup functions in the new contract won’t run
Events that mark the upgrade won’t be emitted
Users may see broken or missing data after the upgrade
Manual review of the contract
Understanding of the UUPS and ERC1967 proxy patterns
Limit upgrades to the graduateAndUpgrade()
function by tracking approved upgrades. We can do this by storing the allowed address and checking it in _authorizeUpgrade()
:
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.