Normal behavior: A UUPS proxy should be initialized atomically (in the same transaction as deployment, via the proxy's constructor data) so only the legitimate deployer can ever become owner.
Issue: script/DeployThunderLoan.s.sol deploys the proxy with empty init data (new ERC1967Proxy(address(thunderLoan), "")) and never calls initialize(). The proxy is left uninitialized, and initialize() has no caller restriction beyond the one-time initializer modifier. Any external account can call initialize() first; __Ownable_init() then sets that caller as owner.
Likelihood: Medium
The proxy is deployed uninitialized; anyone monitoring the chain can submit initialize() before the deployer’s follow-up transaction is mined.
Impact: High
The attacker becomes the permanent owner, controlling _authorizeUpgrade (can upgrade to a malicious implementation and drain all funds), setAllowedToken, and updateFlashLoanFee — full protocol takeover.
Run forge test --match-contract PoC_InitializerFrontrun -vv:
Result: tl.owner() == attacker.
Initialize atomically in the proxy constructor so initialization cannot be front-run:
The contest is live. Earn rewards by submitting a finding.
Submissions are being reviewed by our AI judge. Results will be available in a few minutes.
View all submissionsThe contest is complete and the rewards are being distributed.