Both ThunderLoan.sol
and ThunderLoanUpgraded.sol
utilize an upgradeable proxy pattern which necessitates the use of initialization functions instead of constructors. The initialize
functions are designed to set up the contract state for the first time.
However, these functions are marked as external
and guarded only by the initializer
modifier. While this modifier is intended to ensure that the function can only be called once, it does not prevent the function from being called by any external entity before the rightful owner has the chance to do so. This opens up the possibility for an attacker to front-run the transaction that sets up the contract, allowing them to initialize the contract with potentially malicious intent.
For instance, the initialize
function in ThunderLoanUpgraded.sol
sets important contract parameters, such as the fee structure and the address of related contracts tswapAddress
:
The same vulnerability is present in the initial ThunderLoan.sol
contract:
If an attacker successfully front-runs the initialization transaction, they could manipulate critical contract parameters, as the tswapAddress
address parameter which would set the s_poolFactory = poolFactoryAddress
in OracleUpgradeable.sol
to a potentially malicious contract address owned by the attacker.
This could lead to loss of funds or denial of service and significantly damage the protocol's credibility and trustworthiness.
To mitigate this risk, the ThunderLoan protocol should employ a deployment strategy that prevents the possibility of front-running. One effective approach is the use of a factory pattern where the initialization function can only be called by the factory contract, which in turn is controlled by the protocol's deployer. Alternatively, the deployment process could include measures such as using private transactions or gas price strategies to make front-running less feasible.
Short term, the initialization function should be restricted to being called by only a predetermined address, such as the deployer's address. This can be enforced by adding an additional condition in the initialize
function that checks if the sender of the transaction (msg.sender
) is the deployer or a specific authorized address.
In the long term, consider automating the deployment and initialization process through a secure factory contract or an initialization script that minimizes the window for front-running opportunities.
Here is an example of how the initialize
function could be modified to prevent unauthorized access:
The deployerAddress
should be set to the address of the account that deploys the contract and is intended to run the initialization. This simple check ensures that only the deployer can initialize the contract, thereby closing the vulnerability to front-running attacks.
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.