Thunder Loan

AI First Flight #7
Beginner FriendlyFoundryDeFiOracle
EXP
View results
Submission Details
Impact: high
Likelihood: medium
Invalid

unprotected initialize on the proxy can be front-run to seize ownership

Description

ThunderLoan is deployed behind an ERC1967Proxy constructed with empty init data, so initialize runs in a separate transaction after deployment. initialize is protected only by the initializer modifier — anyone may call it first.

An attacker who front-runs the deployer's initialize becomes owner. The owner authorizes upgrades (_authorizeUpgrade is onlyOwner), so a hijacked initialization is a full takeover: upgrade to a malicious implementation and drain every AssetToken.

function initialize(address tswapAddress) external initializer {
@> __Ownable_init(); // owner = msg.sender, i.e. whoever calls initialize first
__UUPSUpgradeable_init();
__Oracle_init(tswapAddress);
}

Risk

Likelihood: Medium

  • There is a public window between proxy deployment and initialization; front-running that tx is routine mempool activity.

Impact: High

  • Attacker becomes owner and can upgrade the implementation to drain all deposits.

Proof of Concept

The test reproduces the exact deployment shape (an ERC1967Proxy built with empty _data), then has attacker call initialize before the deployer does. attacker ends up as owner, which is sufficient to authorize a malicious upgrade:

function test_G2_unprotectedInitializerFrontRun() public {
ThunderLoan impl = new ThunderLoan();
ERC1967Proxy p = new ERC1967Proxy(address(impl), ""); // empty data, as deployed
ThunderLoan tl = ThunderLoan(address(p));
vm.prank(attacker);
tl.initialize(address(mockPoolFactory)); // attacker calls first
assertEq(tl.owner(), attacker);
}

Recommended Mitigation

Initialize atomically: pass the initialize calldata as the proxy's _data in the ERC1967Proxy constructor so deployment and initialization happen in one transaction and cannot be front-run.

Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge about 4 hours ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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

Give us feedback!